| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Hpgsql.Encoding
Description
Encoding and decoding fields and rows
This module contains a collection of functions, classes, and instances that can help you build encoders and decoders for your Haskell types.
Here's an example:
data Person = Person { name :: Text, born :: Day, heightMeters :: Double }
deriving stock Generic
deriving anyclass FromPgRow
persons :: [Person] <- query conn "SELECT * FROM persons"Note that Hpgsql's RowDecoder does not have a Monad instance because that allows it to
type check query results and field counts even when queries return zero rows. If you need
to write a row decoder that is monadic (because decoding can change depending on the values
of fields), check Hpgsql.Encoding.RowDecoderMonadic.
Synopsis
- class FromPgField a where
- fieldDecoder :: FieldDecoder a
- data FieldDecoder a = FieldDecoder {
- fieldValueDecoder :: FieldInfo -> Maybe ByteString -> Either String a
- allowedPgTypes :: FieldInfo -> Bool
- data FieldInfo = FieldInfo {
- fieldTypeOid :: !Oid
- fieldName :: !(Maybe Text)
- encodingContext :: !EncodingContext
- class FromPgRow a where
- rowDecoder :: RowDecoder a
- data RowDecoder a = RowDecoder {
- fullRowDecoder :: [FieldInfo] -> Parser a
- rowColumnsTypeCheck :: [FieldInfo] -> [(FieldInfo, Bool)]
- numExpectedColumns :: !Int
- singleField :: FieldDecoder a -> RowDecoder a
- nullableField :: FieldDecoder a -> FieldDecoder (Maybe a)
- genericFromPgRow :: (Generic a, ProductTypeDecoder (Rep a)) => RowDecoder a
- class ToPgField a where
- fieldEncoder :: FieldEncoder a
- data FieldEncoder a = FieldEncoder {
- toTypeOid :: !(EncodingContext -> Maybe Oid)
- toPgField :: !(EncodingContext -> a -> BinaryField)
- class ToPgRow a where
- rowEncoder :: RowEncoder a
- data RowEncoder a = RowEncoder {
- toPgParams :: !(a -> [EncodingContext -> (Maybe Oid, BinaryField)])
- toTypeOids :: !(Proxy a -> [EncodingContext -> Maybe Oid])
- toBinaryCopyBytes :: !(EncodingContext -> a -> Builder)
- newtype EncodingContext = EncodingContext {
- typeInfoCache :: TypeInfoCache
- genericToPgRow :: (Generic a, ProductTypeEncoder (Rep a)) => RowEncoder a
- newtype LowerCasedPgEnum a = LowerCasedPgEnum a
- genericEnumFieldDecoder :: (Generic a, EnumDecoder (Rep a)) => (Text -> Text) -> FieldDecoder a
- genericEnumFieldEncoder :: (Generic a, EnumEncoder (Rep a)) => (Text -> Text) -> a -> ByteString
- compositeTypeDecoder :: RowDecoder a -> FieldDecoder a
- compositeTypeEncoder :: RowEncoder a -> FieldEncoder a
- typeFieldDecoder :: (FieldInfo -> Bool) -> FieldDecoder a -> FieldDecoder a
- typeFieldEncoder :: (EncodingContext -> Maybe Oid) -> FieldEncoder a -> FieldEncoder a
- typeOidWithName :: Text -> EncodingContext -> Maybe Oid
- typeMustBeNamed :: Text -> FieldInfo -> Bool
- rawBytesFieldDecoder :: FieldDecoder ByteString
- untypedFieldEncoder :: (EncodingContext -> a -> BinaryField) -> FieldEncoder a
- toPgVectorField :: (Foldable f, ToPgField a) => EncodingContext -> f a -> BinaryField
- arrayField :: forall a f. Monoid (f a) => (forall (m :: Type -> Type). Monad m => Int -> m a -> m (f a)) -> FieldDecoder a -> FieldDecoder (f a)
Decoding
class FromPgField a where Source #
Methods
fieldDecoder :: FieldDecoder a Source #
Instances
data FieldDecoder a Source #
A decoder for a single field/column.
Constructors
| FieldDecoder | |
Fields
| |
Instances
| Functor FieldDecoder Source # | |
Defined in Hpgsql.Encoding Methods fmap :: (a -> b) -> FieldDecoder a -> FieldDecoder b # (<$) :: a -> FieldDecoder b -> FieldDecoder a # | |
| Semigroup (FieldDecoder a) Source # | `f1 <> f2` produces a |
Defined in Hpgsql.Encoding Methods (<>) :: FieldDecoder a -> FieldDecoder a -> FieldDecoder a # sconcat :: NonEmpty (FieldDecoder a) -> FieldDecoder a # stimes :: Integral b => b -> FieldDecoder a -> FieldDecoder a # | |
Constructors
| FieldInfo | |
Fields
| |
class FromPgRow a where Source #
Minimal complete definition
Nothing
Methods
rowDecoder :: RowDecoder a Source #
default rowDecoder :: (Generic a, ProductTypeDecoder (Rep a)) => RowDecoder a Source #
Instances
data RowDecoder a Source #
Constructors
| RowDecoder | |
Fields
| |
Instances
| Applicative RowDecoder Source # | |||||
Defined in Hpgsql.Encoding Methods pure :: a -> RowDecoder a # (<*>) :: RowDecoder (a -> b) -> RowDecoder a -> RowDecoder b # liftA2 :: (a -> b -> c) -> RowDecoder a -> RowDecoder b -> RowDecoder c # (*>) :: RowDecoder a -> RowDecoder b -> RowDecoder b # (<*) :: RowDecoder a -> RowDecoder b -> RowDecoder a # | |||||
| Functor RowDecoder Source # | |||||
Defined in Hpgsql.Encoding Methods fmap :: (a -> b) -> RowDecoder a -> RowDecoder b # (<$) :: a -> RowDecoder b -> RowDecoder a # | |||||
| (TypeError ('Text "RowDecoder does not have a Monad instance in Hpgsql because Hpgsql type-checks the result types of queries before having access to even the first data row. Use the Applicative class to write your instances or use the Monadic decoding variants.") :: Constraint) => Monad RowDecoder Source # | |||||
Defined in Hpgsql.Encoding Methods (>>=) :: RowDecoder a -> (a -> RowDecoder b) -> RowDecoder b # (>>) :: RowDecoder a -> RowDecoder b -> RowDecoder b # return :: a -> RowDecoder a # | |||||
| Generic (RowDecoder a) Source # | |||||
Defined in Hpgsql.Encoding Associated Types
| |||||
| type Rep (RowDecoder a) Source # | |||||
Defined in Hpgsql.Encoding | |||||
singleField :: FieldDecoder a -> RowDecoder a Source #
nullableField :: FieldDecoder a -> FieldDecoder (Maybe a) Source #
A FieldDecoder that accepts and decodes SQL NULLs into Nothing values
for a given decoder.
genericFromPgRow :: (Generic a, ProductTypeDecoder (Rep a)) => RowDecoder a Source #
Derives FromPgRow generically.
Encoding
Keep in mind that Haskell's Int is an Int64 on most
hardware, which is a mismatch for the commonly used 32-bit
integer PostgreSQL type.
This is not a problem when decoding because Hpgsql can decode
integer into Haskell's Int, but when encoding PostgreSQL
will understandably not accept a larger type.
class ToPgField a where Source #
Methods
fieldEncoder :: FieldEncoder a Source #
Instances
data FieldEncoder a Source #
Constructors
| FieldEncoder | |
Fields
| |
Instances
| Contravariant FieldEncoder Source # | |
Defined in Hpgsql.Encoding Methods contramap :: (a' -> a) -> FieldEncoder a -> FieldEncoder a' # (>$) :: b -> FieldEncoder b -> FieldEncoder a # | |
class ToPgRow a where Source #
Minimal complete definition
Nothing
Methods
rowEncoder :: RowEncoder a Source #
default rowEncoder :: (Generic a, ProductTypeEncoder (Rep a)) => RowEncoder a Source #
Instances
data RowEncoder a Source #
Constructors
| RowEncoder | |
Fields
| |
Instances
| Contravariant RowEncoder Source # | |
Defined in Hpgsql.Encoding Methods contramap :: (a' -> a) -> RowEncoder a -> RowEncoder a' # (>$) :: b -> RowEncoder b -> RowEncoder a # | |
newtype EncodingContext Source #
Constructors
| EncodingContext | |
Fields
| |
genericToPgRow :: (Generic a, ProductTypeEncoder (Rep a)) => RowEncoder a Source #
PostgreSQL enums
newtype LowerCasedPgEnum a Source #
For the very common case of a Haskell enum matching a custom postgres enum type that has its values all as lower case strings, this newtype can help you derive instances as such:
data Mood = Sad | Ok | Happy deriving stock (Generic) deriving (FromPgField, ToPgField) via (LowerCasedPgEnum Mood)
And this would match the Postgres equivalent:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');If you run into PostgreSQL type inference problems with this, you can
write instances manually with genericEnumFieldDecoder, genericEnumFieldEncoder,
typeFieldEncoder, and typeFieldDecoder.
Constructors
| LowerCasedPgEnum a |
Instances
| (Generic a, EnumDecoder (Rep a)) => FromPgField (LowerCasedPgEnum a) Source # | |
Defined in Hpgsql.Encoding Methods | |
| (Generic a, EnumEncoder (Rep a)) => ToPgField (LowerCasedPgEnum a) Source # | |
Defined in Hpgsql.Encoding Methods | |
genericEnumFieldDecoder Source #
Arguments
| :: (Generic a, EnumDecoder (Rep a)) | |
| => (Text -> Text) | A function that takes in the Haskell constructor name and returns the textual representation of the enum in postgres |
| -> FieldDecoder a |
One of the functions behind LowerCasedPgEnum, but you can decide
how to map your type's constructor names arbitrarily, which can be
useful if you're not using lowercase values in your postgres enums.
genericEnumFieldEncoder Source #
Arguments
| :: (Generic a, EnumEncoder (Rep a)) | |
| => (Text -> Text) | A function that takes in the Haskell constructor name and returns the textual representation of the enum in postgres |
| -> a | |
| -> ByteString |
One of the functions behind LowerCasedPgEnum, but you can decide
how to map your type's constructor names arbitrarily, which can be
useful if you're not using lowercase values in your postgres enums.
PostgreSQL composite types
compositeTypeDecoder :: RowDecoder a -> FieldDecoder a Source #
Allows you to create a FieldDecoder for composite types.
For a type such as:
CREATE TYPE int_and_bool AS (numfield INT, boolfield BOOL);
You can define a Haskell type as such:
data IntAndBool = IntAndBool Int Bool instance FromPgField IntAndBool where fieldDecoder = compositeTypeDecoder rowDecoder <&> \(i, b) -> IntAndBool i b
compositeTypeEncoder :: RowEncoder a -> FieldEncoder a Source #
Allows you to create a FieldEncoder for composite types.
For a type such as:
CREATE TYPE int_and_bool AS (numfield INT, boolfield BOOL);
You can define a Haskell type as such:
data IntAndBool = IntAndBool Int Bool
instance ToPgField IntAndBool where
fieldEncoder = typeFieldEncoder (typeOidWithName "int_and_bool")
$ compositeTypeEncoder $ contramap (\(IntAndBool i b) -> (fromIntegral i :: Int32, b)) rowEncoderDriving PostgreSQL type inference
typeFieldDecoder :: (FieldInfo -> Bool) -> FieldDecoder a -> FieldDecoder a Source #
Allows you to specify a type (and other checks, possibly) for a FieldDecoder.
This can be useful to ensure you're not accidentally decoding a different type.
data MyEnum = Val1 | Val2 | Val3
myEnumFieldDecoderWithTypeInfoCheck :: FieldDecoder MyEnum
myEnumFieldDecoderWithTypeInfoCheck =
let convert = \case
"val1" -> Val1
"val2" -> Val2
"val3" -> Val3
_ -> error "Invalid value for MyEnum"
in typeFieldDecoder
(typeMustBeNamed "my_enum")
$ convert <$> rawBytesFieldDecoderThis will work unless you use non-default flags in your connection options.
typeFieldEncoder :: (EncodingContext -> Maybe Oid) -> FieldEncoder a -> FieldEncoder a Source #
Allows you to specify a type for a FieldEncoder. This can be useful to avoid letting postgres infer types itself, which can cause errors. For example:
data MyEnum = Val1 | Val2 | Val3
myEnumFieldDecoderWithTypeInfoCheck :: FieldEncoder MyEnum
myEnumFieldDecoderWithTypeInfoCheck =
let convert = \case
Val1 -> "val1" :: Text
Val2 -> "val2"
Val3 -> "val3"
in typeFieldEncoder
(typeOidWithName "my_enum")
$ contramap convert fieldEncoderThis will work unless you use non-default flags in your connection options.
typeOidWithName :: Text -> EncodingContext -> Maybe Oid Source #
Others
rawBytesFieldDecoder :: FieldDecoder ByteString Source #
A decoder that accepts any PG type and returns the object's postgres' binary representation as a ByteString.
untypedFieldEncoder :: (EncodingContext -> a -> BinaryField) -> FieldEncoder a Source #
Returns a FieldEncoder that is sent without a type OID in queries.
This means postgres will try to infer the type of these arguments.
Check typedFieldEncoder if you're interested in encoding your custom types,
you probably don't need this.
toPgVectorField :: (Foldable f, ToPgField a) => EncodingContext -> f a -> BinaryField Source #
Returns a field-encoding function for a vector-like Foldable (e.g. Lists and Vector itself).
arrayField :: forall a f. Monoid (f a) => (forall (m :: Type -> Type). Monad m => Int -> m a -> m (f a)) -> FieldDecoder a -> FieldDecoder (f a) Source #
A FieldDecoder that accepts and decodes Postgres arrays.