on dev help sign in
move PG to Binary
commit392022c18184426e3ed32c22e9bfd5672ffd691a authorEitan Chatav <eitan@Eitans-MacBook-Pro.local> dateFri 1 Mar 2019, 15:54 (8 years ago) parente50f5ca
4 files changed+163−169
squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+160−6 squeal-postgresql/src/Squeal/PostgreSQL/Manipulation.hs+1 squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+1 squeal-postgresql/src/Squeal/PostgreSQL/Schema.hs+1−163
squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+160−6
@@ -1,8 +1,3 @@
1−{-# LANGUAGE
2− TypeFamilies
3− , UndecidableSuperClasses
4−#-}
5−
61{-|
72Module: Squeal.PostgreSQL.Binary
83Description: Binary encoding and decoding
@@ -199,13 +194,22 @@ True
199194 , MultiParamTypeClasses
200195 , ScopedTypeVariables
201196 , TypeApplications
197+ , TypeFamilies
202198 , TypeInType
203199 , TypeOperators
204200 , UndecidableInstances
201+ , UndecidableSuperClasses
205202#-}
206203
207204module Squeal.PostgreSQL.Binary
208− ( Json (..)
205+ ( -- * PG type families
206+ PG
207+ , NullPG
208+ , TuplePG
209+ , RowPG
210+ , LabelsPG
211+ -- * Storage newtypes
212+ , Json (..)
209213 , Jsonb (..)
210214 , Composite (..)
211215 , Enumerated (..)
@@ -226,9 +230,12 @@ import BinaryParser
226230import ByteString.StrictBuilder (builderLength, int32BE, int64BE, word32BE)
227231import Control.Arrow (left)
228232import Control.Monad
233+import Data.Aeson (Value)
234+import Data.ByteString (ByteString)
229235import Data.Int
230236import Data.Kind
231237import Data.Scientific
238+import Data.Text (Text)
232239import Data.Time
233240import Data.UUID.Types
234241import Data.Vector (Vector)
@@ -246,6 +253,7 @@ import qualified Data.Text.Lazy as Lazy (Text)
246253import qualified Data.Text as Strict (Text)
247254import qualified Data.Text as Strict.Text
248255import qualified Data.Vector as Vector
256+import qualified Generics.SOP.Type.Metadata as Type
249257import qualified GHC.Generics as GHC
250258import qualified PostgreSQL.Binary.Decoding as Decoding
251259import qualified PostgreSQL.Binary.Encoding as Encoding
@@ -773,3 +781,149 @@ type family FixPG (hask :: Type) :: NullityType where
773781 FixPG (x,x,x,x,x,x,x,x,x,x) = FixPG x
774782 FixPG (x,x,x,x,x,x,x,x,x,x,x) = FixPG x
775783 FixPG x = NullPG x
784+
785+{- | The `PG` type family embeds a subset of Haskell types
786+as Postgres types. As an open type family, `PG` is extensible.
787+
788+>>> :kind! PG LocalTime
789+PG LocalTime :: PGType
790+= 'PGtimestamp
791+
792+>>> newtype MyDouble = My Double
793+>>> :set -XTypeFamilies
794+>>> type instance PG MyDouble = 'PGfloat8
795+-}
796+type family PG (hask :: Type) :: PGType
797+type instance PG Bool = 'PGbool
798+type instance PG Int16 = 'PGint2
799+type instance PG Int32 = 'PGint4
800+type instance PG Int64 = 'PGint8
801+type instance PG Word16 = 'PGint2
802+type instance PG Word32 = 'PGint4
803+type instance PG Word64 = 'PGint8
804+type instance PG Scientific = 'PGnumeric
805+type instance PG Float = 'PGfloat4
806+type instance PG Double = 'PGfloat8
807+type instance PG Char = 'PGchar 1
808+type instance PG Text = 'PGtext
809+type instance PG Lazy.Text = 'PGtext
810+type instance PG String = 'PGtext
811+type instance PG ByteString = 'PGbytea
812+type instance PG Lazy.ByteString = 'PGbytea
813+type instance PG LocalTime = 'PGtimestamp
814+type instance PG UTCTime = 'PGtimestamptz
815+type instance PG Day = 'PGdate
816+type instance PG TimeOfDay = 'PGtime
817+type instance PG (TimeOfDay, TimeZone) = 'PGtimetz
818+type instance PG DiffTime = 'PGinterval
819+type instance PG UUID = 'PGuuid
820+type instance PG (NetAddr IP) = 'PGinet
821+type instance PG Value = 'PGjson
822+
823+{-| The `LabelsPG` type family calculates the constructors of a
824+Haskell enum type.
825+
826+>>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
827+>>> instance Generic Schwarma
828+>>> instance HasDatatypeInfo Schwarma
829+>>> :kind! LabelsPG Schwarma
830+LabelsPG Schwarma :: [Type.ConstructorName]
831+= '["Beef", "Lamb", "Chicken"]
832+-}
833+type family LabelsPG (hask :: Type) :: [Type.ConstructorName] where
834+ LabelsPG hask =
835+ ConstructorNamesOf (ConstructorsOf (DatatypeInfoOf hask))
836+
837+{- | `RowPG` turns a Haskell record type into a `RowType`.
838+
839+>>> data Person = Person { name :: Text, age :: Int32 } deriving GHC.Generic
840+>>> instance Generic Person
841+>>> instance HasDatatypeInfo Person
842+>>> :kind! RowPG Person
843+RowPG Person :: [(Symbol, NullityType)]
844+= '["name" ::: 'NotNull 'PGtext, "age" ::: 'NotNull 'PGint4]
845+-}
846+type family RowPG (hask :: Type) :: RowType where
847+ RowPG (hask1, hask2) = Join (RowPG hask1) (RowPG hask2)
848+ RowPG (hask1, hask2, hask3) =
849+ Join (RowPG hask1) (RowPG (hask2, hask3))
850+ RowPG (hask1, hask2, hask3, hask4) =
851+ Join (RowPG hask1) (RowPG (hask2, hask3, hask4))
852+ RowPG (hask1, hask2, hask3, hask4, hask5) =
853+ Join (RowPG hask1) (RowPG (hask2, hask3, hask4, hask5))
854+ RowPG (P (col ::: head)) = '[col ::: NullPG head]
855+ RowPG hask = RowOf (RecordCodeOf hask)
856+
857+type family RowOf (fields :: [(Symbol, Type)]) :: RowType where
858+ RowOf '[] = '[]
859+ RowOf (field ': fields) = FieldPG field ': RowOf fields
860+
861+type family FieldPG (field :: (Symbol, Type)) :: (Symbol, NullityType) where
862+ FieldPG (field ::: hask) = field ::: NullPG hask
863+
864+{- | `NullPG` turns a Haskell type into a `NullityType`.
865+
866+>>> :kind! NullPG Double
867+NullPG Double :: NullityType
868+= 'NotNull 'PGfloat8
869+>>> :kind! NullPG (Maybe Double)
870+NullPG (Maybe Double) :: NullityType
871+= 'Null 'PGfloat8
872+-}
873+type family NullPG (hask :: Type) :: NullityType where
874+ NullPG (Maybe hask) = 'Null (PG hask)
875+ NullPG hask = 'NotNull (PG hask)
876+
877+{- | `TuplePG` turns a Haskell tuple type (including record types) into
878+the corresponding list of `NullityType`s.
879+
880+>>> :kind! TuplePG (Double, Maybe Char)
881+TuplePG (Double, Maybe Char) :: [NullityType]
882+= '[ 'NotNull 'PGfloat8, 'Null ('PGchar 1)]
883+-}
884+type family TuplePG (hask :: Type) :: [NullityType] where
885+ TuplePG hask = TupleOf (TupleCodeOf hask (Code hask))
886+
887+type family TupleOf (tuple :: [Type]) :: [NullityType] where
888+ TupleOf '[] = '[]
889+ TupleOf (hask ': tuple) = NullPG hask ': TupleOf tuple
890+
891+type family TupleCodeOf (hask :: Type) (code :: [[Type]]) :: [Type] where
892+ TupleCodeOf hask '[tuple] = tuple
893+ TupleCodeOf hask '[] =
894+ TypeError
895+ ( 'Text "The type `" ':<>: 'ShowType hask ':<>: 'Text "' is not a tuple type."
896+ ':$$: 'Text "It is a void type with no constructors."
897+ )
898+ TupleCodeOf hask (_ ': _ ': _) =
899+ TypeError
900+ ( 'Text "The type `" ':<>: 'ShowType hask ':<>: 'Text "' is not a tuple type."
901+ ':$$: 'Text "It is a sum type with more than one constructor."
902+ )
903+
904+-- | Calculates constructors of a datatype.
905+type family ConstructorsOf (datatype :: Type.DatatypeInfo)
906+ :: [Type.ConstructorInfo] where
907+ ConstructorsOf ('Type.ADT _module _datatype constructors) =
908+ constructors
909+ ConstructorsOf ('Type.Newtype _module _datatype constructor) =
910+ '[constructor]
911+
912+-- | Calculates the name of a nullary constructor, otherwise
913+-- generates a type error.
914+type family ConstructorNameOf (constructors :: Type.ConstructorInfo)
915+ :: Type.ConstructorName where
916+ ConstructorNameOf ('Type.Constructor name) = name
917+ ConstructorNameOf ('Type.Infix name _assoc _fix) = TypeError
918+ ('Text "ConstructorNameOf error: non-nullary constructor "
919+ ':<>: 'Text name)
920+ ConstructorNameOf ('Type.Record name _fields) = TypeError
921+ ('Text "ConstructorNameOf error: non-nullary constructor "
922+ ':<>: 'Text name)
923+
924+-- | Calculate the names of nullary constructors.
925+type family ConstructorNamesOf (constructors :: [Type.ConstructorInfo])
926+ :: [Type.ConstructorName] where
927+ ConstructorNamesOf '[] = '[]
928+ ConstructorNamesOf (constructor ': constructors) =
929+ ConstructorNameOf constructor ': ConstructorNamesOf constructors
squeal-postgresql/src/Squeal/PostgreSQL/Manipulation.hs+1
@@ -62,6 +62,7 @@ import GHC.TypeLits (KnownSymbol)
6262import qualified Generics.SOP as SOP
6363import qualified GHC.Generics as GHC
6464
65+import Squeal.PostgreSQL.Binary
6566import Squeal.PostgreSQL.Expression
6667import Squeal.PostgreSQL.Render
6768import Squeal.PostgreSQL.Query
squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+1
@@ -111,6 +111,7 @@ import GHC.TypeLits
111111
112112import qualified GHC.Generics as GHC
113113
114+import Squeal.PostgreSQL.Binary
114115import Squeal.PostgreSQL.Expression
115116import Squeal.PostgreSQL.Render
116117import Squeal.PostgreSQL.Schema
squeal-postgresql/src/Squeal/PostgreSQL/Schema.hs+1−163
@@ -40,11 +40,6 @@ module Squeal.PostgreSQL.Schema
4040 , NullityType (..)
4141 , RowType
4242 , FromType
43− -- * Haskell to Postgres Types
44− , PG
45− , NullPG
46− , TuplePG
47− , RowPG
4843 -- * Schema Types
4944 , ColumnType
5045 , ColumnsType
@@ -78,7 +73,6 @@ module Squeal.PostgreSQL.Schema
7873 -- * Enumerated Labels
7974 , IsPGlabel (..)
8075 , PGlabel (..)
81− , LabelsPG
8276 -- * Grouping
8377 , Grouping (..)
8478 , GroupedBy
@@ -123,29 +117,18 @@ module Squeal.PostgreSQL.Schema
123117
124118import Control.Category
125119import Control.DeepSeq
126−import Data.Aeson (Value)
127120import Data.ByteString (ByteString)
128−import Data.Int (Int16, Int32, Int64)
129121import Data.Kind
130122import Data.Monoid hiding (All)
131−import Data.Scientific (Scientific)
132123import Data.String
133−import Data.Text (Text)
134−import Data.Time
135−import Data.Word (Word16, Word32, Word64)
136124import Data.Type.Bool
137−import Data.UUID.Types (UUID)
125+import Data.Word (Word32)
138126import Generics.SOP
139−import Generics.SOP.Record
140127import GHC.OverloadedLabels
141128import GHC.TypeLits
142−import Network.IP.Addr
143129import Prelude hiding (id, (.))
144130
145−import qualified Data.ByteString.Lazy as Lazy
146−import qualified Data.Text.Lazy as Lazy
147131import qualified GHC.Generics as GHC
148−import qualified Generics.SOP.Type.Metadata as Type
149132
150133import Squeal.PostgreSQL.Render
151134
@@ -774,151 +757,6 @@ instance All KnownSymbol labels => RenderSQL (NP PGlabel labels) where
774757 . hcollapse
775758 . hcmap (Proxy @KnownSymbol) (K . renderSQL)
776759
777−{- | The `PG` type family embeds a subset of Haskell types
778−as Postgres types. As an open type family, `PG` is extensible.
779−
780−>>> :kind! PG LocalTime
781−PG LocalTime :: PGType
782−= 'PGtimestamp
783−
784−>>> newtype MyDouble = My Double
785−>>> type instance PG MyDouble = 'PGfloat8
786−-}
787−type family PG (hask :: Type) :: PGType
788−type instance PG Bool = 'PGbool
789−type instance PG Int16 = 'PGint2
790−type instance PG Int32 = 'PGint4
791−type instance PG Int64 = 'PGint8
792−type instance PG Word16 = 'PGint2
793−type instance PG Word32 = 'PGint4
794−type instance PG Word64 = 'PGint8
795−type instance PG Scientific = 'PGnumeric
796−type instance PG Float = 'PGfloat4
797−type instance PG Double = 'PGfloat8
798−type instance PG Char = 'PGchar 1
799−type instance PG Text = 'PGtext
800−type instance PG Lazy.Text = 'PGtext
801−type instance PG String = 'PGtext
802−type instance PG ByteString = 'PGbytea
803−type instance PG Lazy.ByteString = 'PGbytea
804−type instance PG LocalTime = 'PGtimestamp
805−type instance PG UTCTime = 'PGtimestamptz
806−type instance PG Day = 'PGdate
807−type instance PG TimeOfDay = 'PGtime
808−type instance PG (TimeOfDay, TimeZone) = 'PGtimetz
809−type instance PG DiffTime = 'PGinterval
810−type instance PG UUID = 'PGuuid
811−type instance PG (NetAddr IP) = 'PGinet
812−type instance PG Value = 'PGjson
813−
814−{-| The `LabelsPG` type family calculates the constructors of a
815−Haskell enum type.
816−
817−>>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
818−>>> instance Generic Schwarma
819−>>> instance HasDatatypeInfo Schwarma
820−>>> :kind! LabelsPG Schwarma
821−LabelsPG Schwarma :: [Type.ConstructorName]
822−= '["Beef", "Lamb", "Chicken"]
823−-}
824−type family LabelsPG (hask :: Type) :: [Type.ConstructorName] where
825− LabelsPG hask =
826− ConstructorNamesOf (ConstructorsOf (DatatypeInfoOf hask))
827−
828−{- | `RowPG` turns a Haskell record type into a `RowType`.
829−
830−>>> data Person = Person { name :: Text, age :: Int32 } deriving GHC.Generic
831−>>> instance Generic Person
832−>>> instance HasDatatypeInfo Person
833−>>> :kind! RowPG Person
834−RowPG Person :: [(Symbol, NullityType)]
835−= '["name" ::: 'NotNull 'PGtext, "age" ::: 'NotNull 'PGint4]
836−-}
837−type family RowPG (hask :: Type) :: RowType where
838− RowPG (hask1, hask2) = Join (RowPG hask1) (RowPG hask2)
839− RowPG (hask1, hask2, hask3) =
840− Join (RowPG hask1) (RowPG (hask2, hask3))
841− RowPG (hask1, hask2, hask3, hask4) =
842− Join (RowPG hask1) (RowPG (hask2, hask3, hask4))
843− RowPG (hask1, hask2, hask3, hask4, hask5) =
844− Join (RowPG hask1) (RowPG (hask2, hask3, hask4, hask5))
845− RowPG (P (col ::: head)) = '[col ::: NullPG head]
846− RowPG hask = RowOf (RecordCodeOf hask)
847−
848−type family RowOf (fields :: [(Symbol, Type)]) :: RowType where
849− RowOf '[] = '[]
850− RowOf (field ': fields) = FieldPG field ': RowOf fields
851−
852−type family FieldPG (field :: (Symbol, Type)) :: (Symbol, NullityType) where
853− FieldPG (field ::: hask) = field ::: NullPG hask
854−
855−{- | `NullPG` turns a Haskell type into a `NullityType`.
856−
857−>>> :kind! NullPG Double
858−NullPG Double :: NullityType
859−= 'NotNull 'PGfloat8
860−>>> :kind! NullPG (Maybe Double)
861−NullPG (Maybe Double) :: NullityType
862−= 'Null 'PGfloat8
863−-}
864−type family NullPG (hask :: Type) :: NullityType where
865− NullPG (Maybe hask) = 'Null (PG hask)
866− NullPG hask = 'NotNull (PG hask)
867−
868−{- | `TuplePG` turns a Haskell tuple type (including record types) into
869−the corresponding list of `NullityType`s.
870−
871−>>> :kind! TuplePG (Double, Maybe Char)
872−TuplePG (Double, Maybe Char) :: [NullityType]
873−= '[ 'NotNull 'PGfloat8, 'Null ('PGchar 1)]
874−-}
875−type family TuplePG (hask :: Type) :: [NullityType] where
876− TuplePG hask = TupleOf (TupleCodeOf hask (Code hask))
877−
878−type family TupleOf (tuple :: [Type]) :: [NullityType] where
879− TupleOf '[] = '[]
880− TupleOf (hask ': tuple) = NullPG hask ': TupleOf tuple
881−
882−type family TupleCodeOf (hask :: Type) (code :: [[Type]]) :: [Type] where
883− TupleCodeOf hask '[tuple] = tuple
884− TupleCodeOf hask '[] =
885− TypeError
886− ( 'Text "The type `" :<>: 'ShowType hask :<>: 'Text "' is not a tuple type."
887− :$$: 'Text "It is a void type with no constructors."
888− )
889− TupleCodeOf hask (_ ': _ ': _) =
890− TypeError
891− ( 'Text "The type `" :<>: 'ShowType hask :<>: 'Text "' is not a tuple type."
892− :$$: 'Text "It is a sum type with more than one constructor."
893− )
894−
895−-- | Calculates constructors of a datatype.
896−type family ConstructorsOf (datatype :: Type.DatatypeInfo)
897− :: [Type.ConstructorInfo] where
898− ConstructorsOf ('Type.ADT _module _datatype constructors) =
899− constructors
900− ConstructorsOf ('Type.Newtype _module _datatype constructor) =
901− '[constructor]
902−
903−-- | Calculates the name of a nullary constructor, otherwise
904−-- generates a type error.
905−type family ConstructorNameOf (constructors :: Type.ConstructorInfo)
906− :: Type.ConstructorName where
907− ConstructorNameOf ('Type.Constructor name) = name
908− ConstructorNameOf ('Type.Infix name _assoc _fix) = TypeError
909− ('Text "ConstructorNameOf error: non-nullary constructor "
910− ':<>: 'Text name)
911− ConstructorNameOf ('Type.Record name _fields) = TypeError
912− ('Text "ConstructorNameOf error: non-nullary constructor "
913− ':<>: 'Text name)
914−
915−-- | Calculate the names of nullary constructors.
916−type family ConstructorNamesOf (constructors :: [Type.ConstructorInfo])
917− :: [Type.ConstructorName] where
918− ConstructorNamesOf '[] = '[]
919− ConstructorNamesOf (constructor ': constructors) =
920− ConstructorNameOf constructor ': ConstructorNamesOf constructors
921−
922760-- | Is a type a valid JSON key?
923761type PGJsonKey = '[ 'PGint2, 'PGint4, 'PGtext ]
924762