on dev help sign in
arrays
commita19d51e771e4ed42990bd0cda534817702b37840 authorEitan Chatav <eitan.chatav@gmail.com> dateFri 23 Feb 2018, 15:44 (9 years ago) parent3c8ebbb
5 files changed+91−32
squeal-postgresql/exe/Example.hs+16−11 squeal-postgresql/squeal-postgresql.cabal+1 squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+14 squeal-postgresql/src/Squeal/PostgreSQL/Expression.hs+33−21 squeal-postgresql/src/Squeal/PostgreSQL/Schema.hs+27
squeal-postgresql/exe/Example.hs+16−11
@@ -4,6 +4,7 @@
44 , FlexibleContexts
55 , OverloadedLabels
66 , OverloadedStrings
7+ , OverloadedLists
78 , TypeApplications
89 , TypeOperators
910#-}
@@ -12,9 +13,10 @@ module Main (main, main2) where
1213
1314import Control.Monad (void)
1415import Control.Monad.Base (liftBase, MonadBase)
15−import Data.Int (Int32)
16+import Data.Int (Int16, Int32)
1617import Data.Monoid ((<>))
1718import Data.Text (Text)
19+import Data.Vector (Vector)
1820
1921import Squeal.PostgreSQL
2022
@@ -27,6 +29,7 @@ type Schema =
2729 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
2830 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
2931 , "name" ::: 'NoDef :=> 'NotNull 'PGtext
32+ , "vec" ::: 'NoDef :=> 'NotNull ('PGarray 'PGint2)
3033 ]
3134 , "emails" :::
3235 '[ "pk_emails" ::: 'PrimaryKey '["id"]
@@ -42,7 +45,8 @@ setup :: Definition '[] Schema
4245setup =
4346 createTable #users
4447 ( serial `As` #id :*
45− (text & notNull) `As` #name :* Nil )
48+ (text & notNull) `As` #name :*
49+ (array int2 & notNull) `As` #vec :* Nil )
4650 ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil )
4751 >>>
4852 createTable #emails
@@ -56,10 +60,10 @@ setup =
5660teardown :: Definition Schema '[]
5761teardown = dropTable #emails >>> dropTable #users
5862
59−insertUser :: Manipulation Schema '[ 'NotNull 'PGtext]
63+insertUser :: Manipulation Schema '[ 'NotNull 'PGtext, 'NotNull ('PGarray 'PGint2)]
6064 '[ "fromOnly" ::: 'NotNull 'PGint4 ]
6165insertUser = insertRows #users
62− (Default `As` #id :* Set (param @1) `As` #name :* Nil) []
66+ (Default `As` #id :* Set (param @1) `As` #name :* Set (param @2) `As` #vec :* Nil) []
6367 OnConflictDoNothing (Returning (#id `As` #fromOnly :* Nil))
6468
6569insertEmail :: Manipulation Schema '[ 'NotNull 'PGint4, 'Null 'PGtext] '[]
@@ -71,29 +75,30 @@ insertEmail = insertRows #emails
7175
7276getUsers :: Query Schema '[]
7377 '[ "userName" ::: 'NotNull 'PGtext
74− , "userEmail" ::: 'Null 'PGtext ]
78+ , "userEmail" ::: 'Null 'PGtext
79+ , "userVec" ::: 'NotNull ('PGarray 'PGint2)]
7580getUsers = select
76− (#u ! #name `As` #userName :* #e ! #email `As` #userEmail :* Nil)
81+ (#u ! #name `As` #userName :* #e ! #email `As` #userEmail :* #u ! #vec `As` #userVec :* Nil)
7782 ( from (Table (#users `As` #u)
7883 & InnerJoin (Table (#emails `As` #e))
7984 (#u ! #id .== #e ! #user_id)) )
8085
81−data User = User { userName :: Text, userEmail :: Maybe Text }
86+data User = User { userName :: Text, userEmail :: Maybe Text, userVec :: Vector (Maybe Int16) }
8287 deriving (Show, GHC.Generic)
8388instance SOP.Generic User
8489instance SOP.HasDatatypeInfo User
8590
8691users :: [User]
8792users =
88− [ User "Alice" (Just "alice@gmail.com")
89− , User "Bob" Nothing
90− , User "Carole" (Just "carole@hotmail.com")
93+ [ User "Alice" (Just "alice@gmail.com") [Nothing, Just 1]
94+ , User "Bob" Nothing [Just 2, Nothing]
95+ , User "Carole" (Just "carole@hotmail.com") [Just 3]
9196 ]
9297
9398session :: (MonadBase IO pq, MonadPQ Schema pq) => pq ()
9499session = do
95100 liftBase $ Char8.putStrLn "manipulating"
96− idResults <- traversePrepared insertUser (Only . userName <$> users)
101+ idResults <- traversePrepared insertUser ([(userName user, userVec user) | user <- users])
97102 ids <- traverse (fmap fromOnly . getRow (RowNumber 0)) idResults
98103 traversePrepared_ insertEmail (zip (ids :: [Int32]) (userEmail <$> users))
99104 liftBase $ Char8.putStrLn "querying"
squeal-postgresql/squeal-postgresql.cabal+1
@@ -50,6 +50,7 @@ library
5050 , transformers >= 0.5.2.0 && < 1
5151 , transformers-base >= 0.4.4 && < 1
5252 , uuid >= 1.3.13 && < 2
53+ , vector
5354
5455test-suite squeal-postgresql-doctest
5556 default-language: Haskell2010
squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+14
@@ -49,6 +49,7 @@ import Data.Kind
4949import Data.Scientific
5050import Data.Time
5151import Data.UUID
52+import Data.Vector (Vector)
5253import Data.Word
5354import Generics.SOP
5455import GHC.TypeLits
@@ -58,6 +59,7 @@ import qualified Data.ByteString.Lazy as Lazy
5859import qualified Data.ByteString as Strict hiding (unpack)
5960import qualified Data.Text.Lazy as Lazy
6061import qualified Data.Text as Strict
62+import qualified Data.Vector as Vector
6163import qualified GHC.Generics as GHC
6264import qualified PostgreSQL.Binary.Decoding as Decoding
6365import qualified PostgreSQL.Binary.Encoding as Encoding
@@ -113,6 +115,10 @@ instance ToParam UTCTime 'PGtimestamptz where
113115instance ToParam DiffTime 'PGinterval where toParam = K . Encoding.interval_int
114116instance ToParam Value 'PGjson where toParam = K . Encoding.json_ast
115117instance ToParam Value 'PGjsonb where toParam = K . Encoding.jsonb_ast
118+instance (Oid pg, ToParam x pg)
119+ => ToParam (Vector (Maybe x)) ('PGarray pg) where
120+ toParam = K . Encoding.nullableArray_vector
121+ (oid (Proxy @pg)) (unK . toParam @x @pg)
116122
117123-- | A `ToColumnParam` constraint lifts the `ToParam` encoding
118124-- of a `Type` to a `ColumnType`, encoding `Maybe`s to `Null`s. You should
@@ -187,6 +193,14 @@ instance FromValue 'PGinterval DiffTime where
187193 fromValue _ = Decoding.interval_int
188194instance FromValue 'PGjson Value where fromValue _ = Decoding.json_ast
189195instance FromValue 'PGjsonb Value where fromValue _ = Decoding.jsonb_ast
196+instance FromValue pg y => FromValue ('PGarray pg) (Vector (Maybe y)) where
197+ fromValue _ = Decoding.array
198+ (Decoding.dimensionArray Vector.replicateM
199+ (Decoding.nullableValueArray (fromValue (Proxy @pg))))
200+instance FromValue pg y => FromValue ('PGarrayN n pg) (Vector (Maybe y)) where
201+ fromValue _ = Decoding.array
202+ (Decoding.dimensionArray Vector.replicateM
203+ (Decoding.nullableValueArray (fromValue (Proxy @pg))))
190204
191205-- | A `FromColumnValue` constraint lifts the `FromValue` parser
192206-- to a decoding of a @(Symbol, ColumnType)@ to a `Type`,
squeal-postgresql/src/Squeal/PostgreSQL/Expression.hs+33−21
@@ -47,7 +47,9 @@ module Squeal.PostgreSQL.Expression
4747 , isn'tNull
4848 , matchNull
4949 , nullIf
50− -- ** Functions
50+ -- ** Arrays
51+ , array
52+ -- ** Functions
5153 , unsafeBinaryOp
5254 , unsafeUnaryOp
5355 , unsafeFunction
@@ -135,6 +137,8 @@ module Squeal.PostgreSQL.Expression
135137 , inet
136138 , json
137139 , jsonb
140+ , vararray
141+ , arrayN
138142 , notNull
139143 , default_
140144 -- * Re-export
@@ -211,26 +215,6 @@ instance (Has table tables columns, Has column columns ty)
211215 table ! column = UnsafeExpression $
212216 renderAlias table <> "." <> renderAlias column
213217
214−-- | A `Column` is a witness to a `HasColumn` constraint. It's used
215−-- in `Squeel.PostgreSQL.Definition.unique` and other
216−-- `Squeel.PostgreSQL.Definition.TableConstraint`s to witness a
217−-- subcolumns relationship.
218−-- data Column
219−-- (columns :: RelationType)
220−-- (columnty :: (Symbol,NullityType))
221−-- where
222−-- Column
223−-- :: Has column columns ty
224−-- => Alias column
225−-- -> Column columns (column ::: ty)
226−-- deriving instance Show (Column columns columnty)
227−-- deriving instance Eq (Column columns columnty)
228−-- deriving instance Ord (Column columns columnty)
229−
230−-- -- | Render a `Column`.
231−-- renderColumn :: Column columns columnty -> ByteString
232−-- renderColumn (Column column) = renderAlias column
233−
234218{- | A `GroupedBy` constraint indicates that a table qualified column is
235219a member of the auxiliary namespace created by @GROUP BY@ clauses and thus,
236220may be called in an output `Expression` without aggregating.
@@ -360,6 +344,17 @@ nullIf
360344nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
361345 (renderExpression x <> ", " <> renderExpression y)
362346
347+array
348+ :: [Expression tables grouping params ('Null ty)]
349+ -> Expression tables grouping params (nullity ('PGarray ty))
350+array xs = UnsafeExpression $
351+ "ARRAY[" <> commaSeparated (renderExpression <$> xs) <> "]"
352+
353+instance Monoid
354+ (Expression tables grouping params (nullity ('PGarray ty))) where
355+ mempty = array []
356+ mappend = unsafeBinaryOp "||"
357+
363358-- | >>> renderExpression @_ @_ @'[_] $ greatest currentTimestamp [param @1]
364359-- "GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))"
365360greatest
@@ -1163,6 +1158,19 @@ json = UnsafeTypeExpression "json"
11631158-- | binary JSON data, decomposed
11641159jsonb :: TypeExpression ('NoDef :=> 'Null 'PGjsonb)
11651160jsonb = UnsafeTypeExpression "jsonb"
1161+-- | variable length array
1162+vararray
1163+ :: TypeExpression ('NoDef :=> 'Null pg)
1164+ -> TypeExpression ('NoDef :=> 'Null ('PGarray pg))
1165+vararray ty = UnsafeTypeExpression $ renderTypeExpression ty <> "[]"
1166+-- | fixed length array
1167+arrayN
1168+ :: KnownNat n
1169+ => proxy n
1170+ -> TypeExpression ('NoDef :=> 'Null pg)
1171+ -> TypeExpression ('NoDef :=> 'Null ('PGarrayN n pg))
1172+arrayN p ty = UnsafeTypeExpression $
1173+ renderTypeExpression ty <> "[" <> renderNat p <> "]"
11661174
11671175-- | used in `createTable` commands as a column constraint to ensure
11681176-- @NULL@ is not present
@@ -1204,3 +1212,7 @@ instance PGTyped 'PGinterval where pgtype = interval
12041212instance PGTyped 'PGuuid where pgtype = uuid
12051213instance PGTyped 'PGjson where pgtype = json
12061214instance PGTyped 'PGjsonb where pgtype = jsonb
1215+instance PGTyped ty => PGTyped ('PGarray ty) where
1216+ pgtype = vararray (pgtype @ty)
1217+instance (KnownNat n, PGTyped ty) => PGTyped ('PGarrayN n ty) where
1218+ pgtype = arrayN (Proxy @n) (pgtype @ty)
squeal-postgresql/src/Squeal/PostgreSQL/Schema.hs+27
@@ -35,6 +35,7 @@ Embedding of PostgreSQL type and alias system
3535module Squeal.PostgreSQL.Schema
3636 ( -- * Kinds
3737 PGType (..)
38+ , Oid (..)
3839 , NullityType (..)
3940 , ColumnType
4041 , ColumnsType
@@ -89,6 +90,7 @@ import Control.DeepSeq
8990import Data.ByteString
9091import Data.Monoid
9192import Data.String
93+import Data.Word
9294import Generics.SOP (AllZip)
9395import GHC.Generics (Generic)
9496import GHC.Exts
@@ -120,8 +122,33 @@ data PGType
120122 | PGinet -- ^ IPv4 or IPv6 host address
121123 | PGjson -- ^ textual JSON data
122124 | PGjsonb -- ^ binary JSON data, decomposed
125+ | PGarray PGType -- ^ variable length array
126+ | PGarrayN Nat PGType -- ^ fixed length array
123127 | UnsafePGType Symbol -- ^ an escape hatch for unsupported PostgreSQL types
124128
129+class Oid (ty :: PGType) where oid :: proxy ty -> Word32
130+instance Oid 'PGbool where oid _ = 16
131+instance Oid 'PGint2 where oid _ = 21
132+instance Oid 'PGint4 where oid _ = 23
133+instance Oid 'PGint8 where oid _ = 20
134+instance Oid 'PGnumeric where oid _ = 1700
135+instance Oid 'PGfloat4 where oid _ = 700
136+instance Oid 'PGfloat8 where oid _ = 701
137+instance Oid ('PGchar n) where oid _ = 18
138+instance Oid ('PGvarchar n) where oid _ = 1043
139+instance Oid 'PGtext where oid _ = 25
140+instance Oid 'PGbytea where oid _ = 17
141+instance Oid 'PGtimestamp where oid _ = 1114
142+instance Oid 'PGtimestamptz where oid _ = 1184
143+instance Oid 'PGdate where oid _ = 1082
144+instance Oid 'PGtime where oid _ = 1083
145+instance Oid 'PGtimetz where oid _ = 1266
146+instance Oid 'PGinterval where oid _ = 1186
147+instance Oid 'PGuuid where oid _ = 2950
148+instance Oid 'PGinet where oid _ = 869
149+instance Oid 'PGjson where oid _ = 114
150+instance Oid 'PGjsonb where oid _ = 3802
151+
125152-- | `NullityType` encodes the potential presence or definite absence of a
126153-- @NULL@ allowing operations which are sensitive to such to be well typed.
127154data NullityType