5 Commits

Author SHA1 Message Date
826c1e10b1 chore: prepare v1.7.0 2024-06-14 18:04:01 -05:00
fbb1f3b8a5 fix: add interval support 2024-06-14 17:00:22 -05:00
b4a84a3210 chore: prepare v1.6.5 2024-05-22 15:49:37 -05:00
a56add0ffc fix: update node-stream-pipes 2024-05-22 15:49:14 -05:00
7ed85ae22b fix: tagOf is not instanceof 2024-05-22 15:42:43 -05:00
13 changed files with 358 additions and 67 deletions

View File

@@ -259,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.6.4/Effect.Aff.Postgres.Pool#t:Pool
[`Config`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#t:Config
[`Pool.make`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#v:make
[`Pool.end`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#v:end
[`Pool.connect`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#v:connect
[`Pool.destroy`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#v:destroy
[`Pool.release`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Pool#v:release
[`Pool`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#t:Pool
[`Config`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#t:Config
[`Pool.make`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#v:make
[`Pool.end`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#v:end
[`Pool.connect`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#v:connect
[`Pool.destroy`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#v:destroy
[`Pool.release`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Pool#v:release
[`Client`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#t:Client
[`Client.end`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:end
[`Client.make`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:make
[`Client.connected`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:connected
[`Client.query`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:query
[`Client.queryRaw`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:queryRaw
[`Client.exec`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Effect.Aff.Postgres.Client#v:exec
[`Client`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#t:Client
[`Client.end`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:end
[`Client.make`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:make
[`Client.connected`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:connected
[`Client.query`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:query
[`Client.queryRaw`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:queryRaw
[`Client.exec`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Effect.Aff.Postgres.Client#v:exec
[`Range`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Range#t:Range
[`Range.gt`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Range#v:gt
[`Range.gte`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Range#v:gte
[`Range.lt`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Range#v:lt
[`Range.lte`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Range#v:lte
[`Range`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Range#t:Range
[`Range.gt`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Range#v:gt
[`Range.gte`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Range#v:gte
[`Range.lt`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Range#v:lt
[`Range.lte`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Range#v:lte
[`Raw`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Raw#t:Raw
[`Null`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Raw#t:Null
[`Raw`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Raw#t:Raw
[`Null`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Raw#t:Null
[`Serialize`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres#t:Serialize
[`Deserialize`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres#t:Deserialize
[`Rep`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres#t:Rep
[`modifyPgTypes`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres#v:modifyPgTypes
[`Serialize`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres#t:Serialize
[`Deserialize`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres#t:Deserialize
[`Rep`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres#t:Rep
[`modifyPgTypes`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres#v:modifyPgTypes
[`Result`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Result#t:Result
[`FromRow`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Result#t:FromRow
[`FromRows`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Result#t:FromRows
[`Result`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Result#t:Result
[`FromRow`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Result#t:FromRow
[`FromRows`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Result#t:FromRows
[`Query`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Query#t:Query
[`AsQuery`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Query#t:AsQuery
[`Query`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Query#t:Query
[`AsQuery`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Query#t:AsQuery
[`Query.Builder`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Query.Builder#t:Builder
[`Query.Builder.param`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Query.Builder#v:param
[`Query.Builder.build`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Data.Postgres.Query.Builder#v:build
[`Query.Builder`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Query.Builder#t:Builder
[`Query.Builder.param`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Query.Builder#v:param
[`Query.Builder.build`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Data.Postgres.Query.Builder#v:build
[`MonadCursor`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#t:MonadCursor
[`MonadSession`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#t:MonadSession
[`CursorT`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#t:CursorT
[`SessionT`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#t:SessionT
[`PostgresT`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#t:PostgresT
[`cursor`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:cursor
[`session`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:session
[`transaction`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:transaction
[`runPostgres`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:runPostgres
[`query`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:query
[`exec`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:exec
[`exec_`]: https://pursuit.purescript.org//////////////////////////////////////packages/purescript-postgresql/1.6.4/Control.Monad.Postgres#v:exec_
[`MonadCursor`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#t:MonadCursor
[`MonadSession`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#t:MonadSession
[`CursorT`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#t:CursorT
[`SessionT`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#t:SessionT
[`PostgresT`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#t:PostgresT
[`cursor`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:cursor
[`session`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:session
[`transaction`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:transaction
[`runPostgres`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:runPostgres
[`query`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:query
[`exec`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:exec
[`exec_`]: https://pursuit.purescript.org////////////////////////////////////////packages/purescript-postgresql/1.7.0/Control.Monad.Postgres#v:exec_
[`node-postgres`]: https://node-postgres.com/
[`pg-types`]: https://github.com/brianc/node-pg-types/

BIN
bun.lockb

Binary file not shown.

View File

@@ -15,6 +15,8 @@
"decimal.js": "^10.4.3",
"pg": "^8.11.3",
"pg-copy-streams": "^6.0.6",
"pg-listen": "^1.7.0",
"postgres-interval": "1.2.0",
"postgres-range": "^1.1.4"
}
}

View File

@@ -23,7 +23,7 @@ workspace:
- newtype: ">=5.0.0 <6.0.0"
- node-buffer: ">=9.0.0 <10.0.0"
- node-event-emitter: ">=3.0.0 <4.0.0"
- node-stream-pipes: ">=1.2.3 <2.0.0"
- node-stream-pipes: ">=1.5.0 <2.0.0"
- node-streams: ">=9.0.0 <10.0.0"
- nullable: ">=6.0.0 <7.0.0"
- parallel: ">=6.0.0 <7.0.0"
@@ -728,8 +728,8 @@ packages:
- unsafe-coerce
node-stream-pipes:
type: registry
version: 1.2.3
integrity: sha256-lXD3x6+p72uBrRHGHrob2jrrBDakhhZE9O9EYE4aFiE=
version: 1.5.0
integrity: sha256-kca0MJ8Pz4tXNpaw7CdysyNAgdbb8yqH1FhOoYbRm5s=
dependencies:
- aff
- arrays

View File

@@ -1,7 +1,7 @@
package:
name: postgresql
publish:
version: '1.6.4'
version: '1.7.0'
license: 'GPL-3.0-or-later'
location:
githubOwner: 'cakekindel'
@@ -33,7 +33,7 @@ package:
- newtype: ">=5.0.0 <6.0.0"
- node-buffer: ">=9.0.0 <10.0.0"
- node-event-emitter: ">=3.0.0 <4.0.0"
- node-stream-pipes: ">=1.2.3 <2.0.0"
- node-stream-pipes: ">=1.5.0 <2.0.0"
- node-streams: ">=9.0.0 <10.0.0"
- nullable: ">=6.0.0 <7.0.0"
- parallel: ">=6.0.0 <7.0.0"

View File

@@ -0,0 +1,30 @@
import PostgresInterval from 'postgres-interval'
/** @typedef {import('postgres-interval').IPostgresInterval} I */
/** @type {(o: {years: number, months: number, days: number, hours: number, minutes: number, seconds: number, milliseconds: number}) => I} */
export const make = o => Object.assign(PostgresInterval(''), o)
/** @type {(s: string) => () => I} */
export const parse = s => () => PostgresInterval(s)
/** @type {(a: I) => number} */
export const getYears = i => i.years || 0.0
/** @type {(a: I) => number} */
export const getMonths = i => i.months || 0.0
/** @type {(a: I) => number} */
export const getDays = i => i.days || 0.0
/** @type {(a: I) => number} */
export const getMinutes = i => i.minutes || 0.0
/** @type {(a: I) => number} */
export const getHours = i => i.hours || 0.0
/** @type {(a: I) => number} */
export const getSeconds = i => i.seconds || 0.0
/** @type {(a: I) => number} */
export const getMilliseconds = i => i.milliseconds || 0.0

View File

@@ -0,0 +1,88 @@
module Data.Postgres.Interval where
import Prelude
import Data.Int as Int
import Data.Maybe (Maybe(..))
import Data.Newtype (unwrap)
import Data.Time.Duration (class Duration, Days(..), Hours(..), Milliseconds(..), Minutes(..), Seconds(..), convertDuration)
import Effect (Effect)
zero :: IntervalRecord
zero = {years: 0, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0.0}
type IntervalRecord =
{ years :: Int
, months :: Int
, days :: Int
, hours :: Int
, minutes :: Int
, seconds :: Int
, milliseconds :: Number
}
foreign import data Interval :: Type
foreign import make :: IntervalRecord -> Interval
foreign import parse :: String -> Effect Interval
foreign import getYears :: Interval -> Int
foreign import getMonths :: Interval -> Int
foreign import getDays :: Interval -> Int
foreign import getHours :: Interval -> Int
foreign import getMinutes :: Interval -> Int
foreign import getSeconds :: Interval -> Int
foreign import getMilliseconds :: Interval -> Number
toDuration :: forall d. Semigroup d => Duration d => Interval -> Maybe d
toDuration a =
let
includesMonths = getYears a > 0 || getMonths a > 0
days :: d
days = convertDuration $ Days $ Int.toNumber $ getDays a
hours :: d
hours = convertDuration $ Hours $ Int.toNumber $ getHours a
minutes :: d
minutes = convertDuration $ Minutes $ Int.toNumber $ getMinutes a
seconds :: d
seconds = convertDuration $ Seconds $ Int.toNumber $ getSeconds a
milliseconds :: d
milliseconds = convertDuration $ Milliseconds $ getMilliseconds a
in
if includesMonths then Nothing else Just (days <> hours <> minutes <> seconds <> milliseconds)
toRecord :: Interval -> IntervalRecord
toRecord a =
{ years: getYears a
, months: getMonths a
, days: getDays a
, hours: getHours a
, minutes: getMinutes a
, seconds: getSeconds a
, milliseconds: getMilliseconds a
}
fromDuration :: forall d. Duration d => d -> Interval
fromDuration a =
let
millisTotal :: Number
millisTotal = (unwrap :: Milliseconds -> Number) $ convertDuration a
secondFactor = 1000.0
minuteFactor = 60.0 * secondFactor
hourFactor = 60.0 * minuteFactor
dayFactor = 24.0 * hourFactor
days = Int.trunc $ millisTotal / dayFactor
daysRem = millisTotal - (Int.toNumber days * dayFactor)
hours = Int.trunc $ daysRem / hourFactor
hoursRem = daysRem - (Int.toNumber hours * hourFactor)
minutes = Int.trunc $ hoursRem / minuteFactor
minutesRem = hoursRem - (Int.toNumber minutes * minuteFactor)
seconds = Int.trunc $ minutesRem / secondFactor
milliseconds = minutesRem - (Int.toNumber seconds * secondFactor)
in
make {years: 0, months: 0, days, hours, minutes, seconds, milliseconds}

View File

@@ -1,5 +1,24 @@
import Pg from 'pg'
import Range from 'postgres-range'
import { Buffer } from 'buffer'
import PostgresInterval from 'postgres-interval'
/** @type {(a: unknown) => boolean} */
export const isInstanceOfBuffer = a => a instanceof Buffer
/** @type {(a: unknown) => boolean} */
export const isInstanceOfInterval = a => {
return typeof a === 'object'
&& a !== null
&& ('years' in a
|| 'months' in a
|| 'days' in a
|| 'hours' in a
|| 'minutes' in a
|| 'seconds' in a
|| 'milliseconds' in a
)
}
export const modifyPgTypes = () => {
// https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js

View File

@@ -3,23 +3,28 @@ module Data.Postgres where
import Prelude
import Control.Alt ((<|>))
import Control.Monad.Error.Class (liftEither, liftMaybe)
import Control.Monad.Error.Class (liftEither, liftMaybe, throwError)
import Control.Monad.Except (ExceptT, runExceptT)
import Control.Monad.Morph (hoist)
import Control.Monad.Trans.Class (lift)
import Data.Bifunctor (lmap)
import Data.DateTime (DateTime)
import Data.DateTime.Instant (Instant)
import Data.DateTime.Instant as Instant
import Data.List.NonEmpty (NonEmptyList)
import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Postgres.Interval (Interval)
import Data.Postgres.Interval as Interval
import Data.Postgres.Range (Range, __rangeFromRecord, __rangeRawFromRaw, __rangeRawFromRecord, __rangeRawToRecord, __rangeToRecord)
import Data.Postgres.Raw (Null(..), Raw, jsNull)
import Data.Postgres.Raw (unsafeFromForeign, asForeign) as Raw
import Data.RFC3339String as DateTime.ISO
import Data.Time.Duration (Days, Hours, Milliseconds, Minutes, Seconds)
import Data.Traversable (traverse)
import Effect (Effect)
import Effect.Exception (error)
import Foreign (ForeignError(..), unsafeToForeign)
import Foreign (ForeignError(..), tagOf, unsafeFromForeign, unsafeToForeign)
import Foreign as F
import JS.BigInt (BigInt)
import JS.BigInt as BigInt
@@ -40,6 +45,9 @@ derive newtype instance ReadForeign a => ReadForeign (JSON a)
-- | for some types to unmarshal as strings rather than JS values.
foreign import modifyPgTypes :: Effect Unit
foreign import isInstanceOfBuffer :: F.Foreign -> Boolean
foreign import isInstanceOfInterval :: F.Foreign -> Boolean
-- | The serialization & deserialization monad.
type RepT = ExceptT (NonEmptyList ForeignError) Effect
@@ -68,6 +76,9 @@ instance (Serialize a, Deserialize a) => Rep a
unsafeSerializeCoerce :: forall m a. Monad m => a -> m Raw
unsafeSerializeCoerce = pure <<< Raw.unsafeFromForeign <<< F.unsafeToForeign
invalidDuration :: NonEmptyList ForeignError
invalidDuration = pure $ ForeignError $ "Can't convert interval with year or month components to Milliseconds"
instance Serialize Raw where
serialize = pure
@@ -107,10 +118,38 @@ instance Serialize String where
instance Serialize Number where
serialize = unsafeSerializeCoerce
-- | `timestamp`, `timestamptz`
-- | `interval`
instance Serialize DateTime where
serialize = serialize <<< unwrap <<< DateTime.ISO.fromDateTime
-- | `interval`
instance Serialize Interval where
serialize = unsafeSerializeCoerce
-- | `interval`
instance Serialize Milliseconds where
serialize = serialize <<< Interval.fromDuration
-- | `interval`
instance Serialize Seconds where
serialize = serialize <<< Interval.fromDuration
-- | `interval`
instance Serialize Minutes where
serialize = serialize <<< Interval.fromDuration
-- | `interval`
instance Serialize Hours where
serialize = serialize <<< Interval.fromDuration
-- | `interval`
instance Serialize Days where
serialize = serialize <<< Interval.fromDuration
-- | `timestamp`, `timestamptz`
instance Serialize Instant where
serialize = serialize <<< Instant.toDateTime
-- | `Just` -> `a`, `Nothing` -> `NULL`
instance Serialize a => Serialize (Maybe a) where
serialize (Just a) = serialize a
@@ -142,7 +181,41 @@ instance ReadForeign a => Deserialize (JSON a) where
-- | `bytea`
instance Deserialize Buffer where
deserialize = (F.unsafeReadTagged "Buffer") <<< Raw.asForeign
deserialize =
let
notBuffer a = pure $ TypeMismatch (tagOf a) "Buffer"
readBuffer a = when (not $ isInstanceOfBuffer a) (throwError $ notBuffer a) $> unsafeFromForeign a
in
readBuffer <<< Raw.asForeign
-- | `interval`
instance Deserialize Interval where
deserialize =
let
notInterval a = pure $ TypeMismatch (tagOf a) "Interval"
readInterval a = when (not $ isInstanceOfInterval a) (throwError $ notInterval a) $> unsafeFromForeign a
in
readInterval <<< Raw.asForeign
-- | `interval`
instance Deserialize Milliseconds where
deserialize = flip bind (liftMaybe invalidDuration) <<< map Interval.toDuration <<< deserialize
-- | `interval`
instance Deserialize Seconds where
deserialize = flip bind (liftMaybe invalidDuration) <<< map Interval.toDuration <<< deserialize
-- | `interval`
instance Deserialize Minutes where
deserialize = flip bind (liftMaybe invalidDuration) <<< map Interval.toDuration <<< deserialize
-- | `interval`
instance Deserialize Hours where
deserialize = flip bind (liftMaybe invalidDuration) <<< map Interval.toDuration <<< deserialize
-- | `interval`
instance Deserialize Days where
deserialize = flip bind (liftMaybe invalidDuration) <<< map Interval.toDuration <<< deserialize
-- | `int2`, `int4`
instance Deserialize Int where
@@ -176,6 +249,10 @@ instance Deserialize DateTime where
let invalid = pure $ ForeignError $ "Not a valid ISO8601 string: `" <> s <> "`"
liftMaybe invalid $ DateTime.ISO.toDateTime $ wrap s
-- | `timestamp`, `timestamptz`
instance Deserialize Instant where
deserialize = map Instant.fromDateTime <<< deserialize
-- | postgres `array`
instance Deserialize a => Deserialize (Array a) where
deserialize = traverse (deserialize <<< Raw.unsafeFromForeign) <=< F.readArray <<< Raw.asForeign

View File

@@ -39,11 +39,11 @@ stdin q = do
pure Nothing
releaseOnEOS (Just a) = pure (Just a)
pipe = Pipes.mapM releaseOnEOS >-> fromWritable (O.fromBufferWritable stream)
pipe = Pipes.mapM releaseOnEOS >-> fromWritable (O.unsafeFromBufferWritable stream)
err e = do
liftAff $ void $ Client.exec "rollback" client
liftEffect $ Pool.release pool client
throwError e
liftAff $ void $ Client.exec "rollback" client
liftEffect $ Pool.release pool client
throwError e
catchError pipe err
@@ -62,4 +62,4 @@ stdout q = do
let
releaseOnEOS Nothing = liftEffect $ Pool.release pool client $> Nothing
releaseOnEOS (Just a) = pure (Just a)
fromReadable (O.fromBufferReadable stream) >-> Pipes.mapM releaseOnEOS
fromReadable (O.unsafeFromBufferReadable stream) >-> Pipes.mapM releaseOnEOS

View File

@@ -0,0 +1,37 @@
module Test.Data.Postgres.Interval where
import Prelude
import Data.Postgres.Interval as Interval
import Data.Time.Duration (Milliseconds(..))
import Data.Traversable (for_)
import Data.Tuple.Nested (type (/\), (/\))
import Effect.Class (liftEffect)
import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (shouldEqual)
spec :: Spec Unit
spec =
describe "Data.Postgres.Interval" do
it "parse & toRecord" do
p <- liftEffect $ Interval.parse "3 days 04:05:06"
Interval.toRecord p `shouldEqual` Interval.zero {days = 3, hours = 4, minutes = 5, seconds = 6}
it "make & toRecord" do
let p = Interval.make $ Interval.zero {days = 3, hours = 4, minutes = 5, seconds = 6}
Interval.toRecord p `shouldEqual` Interval.zero {days = 3, hours = 4, minutes = 5, seconds = 6}
describe "fromDuration" do
for_
[ Milliseconds 100.0 /\ Interval.zero {milliseconds = 100.0}
, Milliseconds 1000.0 /\ Interval.zero {seconds = 1}
, Milliseconds 1100.0 /\ Interval.zero {seconds = 1, milliseconds = 100.0}
, Milliseconds 60000.0 /\ Interval.zero {minutes = 1}
, Milliseconds 61100.0 /\ Interval.zero {minutes = 1, seconds = 1, milliseconds = 100.0}
, Milliseconds 3600000.0 /\ Interval.zero {hours = 1}
, Milliseconds 3661100.0 /\ Interval.zero {hours = 1, minutes = 1, seconds = 1, milliseconds = 100.0}
, Milliseconds 86400000.0 /\ Interval.zero {days = 1}
, Milliseconds 90061100.0 /\ Interval.zero {days = 1, hours = 1, minutes = 1, seconds = 1, milliseconds = 100.0}
]
\(i /\ o) -> it ("converts " <> show i) do
Interval.toRecord (Interval.fromDuration i) `shouldEqual` o

View File

@@ -2,28 +2,28 @@ module Test.Data.Postgres where
import Prelude
import Control.Monad.Gen (chooseInt, elements, oneOf)
import Control.Monad.Gen (chooseFloat, chooseInt, elements, oneOf)
import Control.Parallel (parTraverse_)
import Data.Array (intercalate)
import Data.Array as Array
import Data.Array.NonEmpty as Array.NonEmpty
import Data.DateTime (DateTime(..), canonicalDate)
import Data.DateTime.Instant as Instant
import Data.Enum (toEnum)
import Data.Foldable (fold)
import Data.Identity (Identity)
import Data.Int as Int
import Data.Maybe (Maybe(..), fromJust, maybe)
import Data.Maybe (Maybe(..), fromJust, fromMaybe)
import Data.Newtype (class Newtype, unwrap, wrap)
import Data.Number (abs) as Number
import Data.Postgres (class Rep)
import Data.Postgres.Interval (Interval)
import Data.Postgres.Interval as Interval
import Data.Postgres.Query.Builder as Q
import Data.Postgres.Raw (Raw, jsNull)
import Data.Postgres.Raw as Raw
import Data.Postgres.Result (class FromRow)
import Data.RFC3339String as DateTime.ISO
import Data.String as String
import Data.Time (Time(..))
import Data.Time.Duration (class Duration, Days, Hours, Milliseconds, Minutes, Seconds)
import Data.Traversable (for, sequence)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
@@ -35,20 +35,45 @@ import Effect.Unsafe (unsafePerformEffect)
import Foreign (Foreign, unsafeToForeign)
import Foreign.Object as Object
import JS.BigInt (BigInt)
import JS.BigInt as BigInt
import Node.Buffer (Buffer)
import Node.Buffer as Buffer
import Partial.Unsafe (unsafePartial)
import Simple.JSON (writeJSON)
import Test.Common (withClient, withPoolClient)
import Test.Common (withPoolClient)
import Test.QuickCheck (class Arbitrary, arbitrary, randomSeed)
import Test.QuickCheck.Gen (sample, vectorOf)
import Test.Spec (Spec, SpecT, around, describe, it, parallel)
import Test.Spec (Spec, SpecT, around, describe, it)
import Test.Spec.Assertions (fail)
foreign import readBigInt64BE :: Buffer -> Effect BigInt
foreign import dbg :: forall a. a -> Effect Unit
newtype GenIntervalSubMonth = GenIntervalSubMonth Interval
derive instance Newtype GenIntervalSubMonth _
instance Arbitrary GenIntervalSubMonth where
arbitrary = do
days <- chooseInt 0 30
hours <- chooseInt 0 23
minutes <- chooseInt 0 59
seconds <- chooseInt 0 59
milliseconds <- chooseFloat 0.0 999.9
pure $ wrap $ Interval.make $ Interval.zero {days = days, hours = hours, minutes = minutes, seconds = seconds, milliseconds = milliseconds}
newtype GenInterval = GenInterval Interval
derive instance Newtype GenInterval _
instance Arbitrary GenInterval where
arbitrary = do
years <- chooseInt 0 10
months <- chooseInt 0 11
days <- chooseInt 0 30
hours <- chooseInt 0 23
minutes <- chooseInt 0 59
seconds <- chooseInt 0 59
milliseconds <- chooseFloat 0.0 999.9
pure $ wrap $ Interval.make {years, months, days, hours, minutes, seconds, milliseconds}
newtype GenSmallInt = GenSmallInt Int
derive instance Newtype GenSmallInt _
@@ -196,6 +221,17 @@ spec =
around withPoolClient
$ describe "Data.Postgres"
$ do
let
durationFromGenInterval :: forall d. Semigroup d => Duration d => Newtype d Number => GenIntervalSubMonth -> d
durationFromGenInterval = fromMaybe (wrap 0.0) <<< Interval.toDuration <<< unwrap
durationEq :: forall d. Duration d => Newtype d Number => d -> d -> Boolean
durationEq a b = Number.abs (unwrap a - unwrap b) <= 0.001
check @Milliseconds @GenIntervalSubMonth { purs: "Milliseconds", sql: "interval", fromArb: durationFromGenInterval, isEq: durationEq}
check @Seconds @GenIntervalSubMonth { purs: "Seconds", sql: "interval", fromArb: durationFromGenInterval, isEq: durationEq}
check @Minutes @GenIntervalSubMonth { purs: "Minutes", sql: "interval", fromArb: durationFromGenInterval, isEq: durationEq}
check @Hours @GenIntervalSubMonth { purs: "Hours", sql: "interval", fromArb: durationFromGenInterval, isEq: durationEq}
check @Days @GenIntervalSubMonth { purs: "Days", sql: "interval", fromArb: durationFromGenInterval, isEq: durationEq}
check @Int @GenSmallInt { purs: "Int", sql: "int2", fromArb: unwrap, isEq: eq }
check @Int { purs: "Int", sql: "int4", fromArb: identity, isEq: eq }
check @String @GenString { purs: "String", sql: "text", fromArb: unwrap, isEq: eq }

View File

@@ -23,6 +23,7 @@ import Node.EventEmitter as Event
import Test.Control.Monad.Postgres as Test.Control.Monad.Postgres
import Test.Data.Postgres as Test.Data.Postgres
import Test.Data.Postgres.Custom as Test.Data.Postgres.Custom
import Test.Data.Postgres.Interval as Test.Data.Postgres.Interval
import Test.Effect.Postgres.Client as Test.Effect.Postgres.Client
import Test.Effect.Postgres.Pool as Test.Effect.Postgres.Pool
import Test.Spec.Reporter (specReporter)
@@ -65,6 +66,7 @@ main = launchAff_ do
$ runSpec [ specReporter ] do
Test.Data.Postgres.Custom.spec
Test.Data.Postgres.spec
Test.Data.Postgres.Interval.spec
Test.Effect.Postgres.Client.spec
Test.Effect.Postgres.Pool.spec
Test.Control.Monad.Postgres.spec