on dev help sign in
Merge pull request #3 from echatav/insert
891ab25Eitan Chatav
squeel-postgresql/src/Squeel/PostgreSQL/Query.hs+48−2
@@ -21,9 +21,11 @@ import Data.Monoid
21import Data.Proxy
21import Data.Proxy
22import Data.String
22import Data.String
23import Data.Vinyl
23import Data.Vinyl
24+import Data.Vinyl.Functor
24import GHC.OverloadedLabels
25import GHC.OverloadedLabels
25import GHC.TypeLits
26import GHC.TypeLits
26
27
28+import qualified Data.ByteString as ByteString
27import qualified Data.ByteString.Char8 as Char8
29import qualified Data.ByteString.Char8 as Char8
28
30
29import Squeel.PostgreSQL.Type
31import Squeel.PostgreSQL.Type
@@ -33,7 +35,7 @@ class HasField label xs x where
33instance {-# OVERLAPPING #-} KnownSymbol label
35instance {-# OVERLAPPING #-} KnownSymbol label
34 => HasField label ('(label, x) ': xs) x where
36 => HasField label ('(label, x) ': xs) x where
35 fieldName _ _ _ = Char8.pack (symbolVal (Proxy @label))
37 fieldName _ _ _ = Char8.pack (symbolVal (Proxy @label))
36−instance HasField label xs y
38+instance {-# OVERLAPPABLE #-} HasField label xs y
37 => HasField label (x ': xs) y where
39 => HasField label (x ': xs) y where
38 fieldName _ _ _ = fieldName (Proxy @label) (Proxy @xs) (Proxy @y)
40 fieldName _ _ _ = fieldName (Proxy @label) (Proxy @xs) (Proxy @y)
39
41
@@ -45,6 +47,9 @@ instance IsLabel label (obj x)
45 => IsLabel label (Aliased obj '(label, x)) where
47 => IsLabel label (Aliased obj '(label, x)) where
46 fromLabel p = fromLabel p `As` fromLabel p
48 fromLabel p = fromLabel p `As` fromLabel p
47
49
50+aliasName :: forall label. KnownSymbol label => Alias label -> ByteString
51+aliasName _ = Char8.pack (symbolVal (Proxy @label))
52+
48alias :: Aliased obj '(label, x) -> Alias label
53alias :: Aliased obj '(label, x) -> Alias label
49alias _ = Alias
54alias _ = Alias
50
55
@@ -59,6 +64,23 @@ renderAliased
59renderAliased render (obj `As` label) =
64renderAliased render (obj `As` label) =
60 render obj <> " AS " <> Char8.pack (symbolVal label)
65 render obj <> " AS " <> Char8.pack (symbolVal label)
61
66
67+class UnzipAliased (xs :: [(Symbol,k)]) where
68+ type Unaliased xs :: [k]
69+ unzipAliased
70+ :: Rec (Aliased obj) xs
71+ -> ([ByteString], Rec obj (Unaliased xs))
72+instance UnzipAliased '[] where
73+ type Unaliased '[] = '[]
74+ unzipAliased _ = ([], RNil)
75+instance (KnownSymbol label, UnzipAliased xs)
76+ => UnzipAliased ( '(label, x) ': xs) where
77+ type Unaliased ( '(label, x) ': xs) = x ': Unaliased xs
78+ unzipAliased (x :& xs) =
79+ let
80+ (labels,values) = unzipAliased xs
81+ in
82+ (aliasName (alias x) : labels, unalias x :& values)
83+
62class AllAliased xs where
84class AllAliased xs where
63 renderAllAliased
85 renderAllAliased
64 :: (forall x. obj x -> ByteString)
86 :: (forall x. obj x -> ByteString)
@@ -232,9 +254,33 @@ subselect selection = Relation
232 , offsetting = Nothing
254 , offsetting = Nothing
233 }
255 }
234
256
257+newtype Insertion ps xss xs = UnsafeInsertion
258+ {renderInsertion :: ByteString }
259+
260+into
261+ :: forall label xss xs ps
262+ . (HasField label xss xs, UnzipAliased xs)
263+ => Proxy label
264+ -> Rec (Aliased (Expression ps '[])) xs
265+ -> Insertion ps xss xs
266+into table values = UnsafeInsertion $ "INTO "
267+ <> fieldName table (Proxy @xss) (Proxy @xs)
268+ <> " (" <> ByteString.intercalate ", " names
269+ <> ") VALUES ("
270+ <> ByteString.intercalate ", "
271+ (recordToList (rmap (Const . renderExpression) exprs))
272+ <> ")"
273+ where
274+ (names,exprs) = unzipAliased values
275+
235newtype Query ps xss yss zs = UnsafeQuery { renderQuery :: ByteString }
276newtype Query ps xss yss zs = UnsafeQuery { renderQuery :: ByteString }
236newtype PreparedQuery ps xss yss zs =
277newtype PreparedQuery ps xss yss zs =
237 UnsafePreparedQuery { renderPreparedQuery :: ByteString }
278 UnsafePreparedQuery { renderPreparedQuery :: ByteString }
238
279
239select :: Selection ps xss ys -> Query ps xss xss ys
280select :: Selection ps xss ys -> Query ps xss xss ys
281+select selection = UnsafeQuery $
240−select selection = UnsafeQuery $ "SELECT " <> renderSelection selection <> ";"
282+ "SELECT " <> renderSelection selection <> ";"
283+
284+insert :: Insertion ps xss xs -> Query ps xss xss '[]
285+insert insertion = UnsafeQuery $
286+ "INSERT " <> renderInsertion insertion <> ";"
squeel-postgresql/test/Squeel/PostgreSQL/QuerySpec.hs+14−11
@@ -2,11 +2,13 @@
2 DataKinds
2 DataKinds
3 , OverloadedLabels
3 , OverloadedLabels
4 , OverloadedStrings
4 , OverloadedStrings
5+ , TypeApplications
5#-}
6#-}
6
7
7module Squeel.PostgreSQL.QuerySpec where
8module Squeel.PostgreSQL.QuerySpec where
8
9
9import Data.Boolean
10import Data.Boolean
11+import Data.Proxy
10import Data.Vinyl
12import Data.Vinyl
11import Test.Hspec
13import Test.Hspec
12
14
@@ -47,21 +49,22 @@ spec = do
47 renderQuery (select (star `from` (table1 `offset` 1 `offset` 2)))
49 renderQuery (select (star `from` (table1 `offset` 1 `offset` 2)))
48 `shouldBe`
50 `shouldBe`
49 "SELECT * FROM table1 OFFSET 1+2;"
51 "SELECT * FROM table1 OFFSET 1+2;"
52+ it "correctly render simple INSERTs" $ do
53+ renderQuery (insert insertion)
54+ `shouldBe`
55+ "INSERT INTO table1 (col1, col2) VALUES (2, 4);"
50
56
57+type Columns = '[ '("col1", 'PGInt4), '("col2", 'PGInt4)]
58+type Tables = '[ '("table1", Columns)]
51
59
52−type Columns =
53− '[ '("col1", 'PGInt4)
54− , '("col2", 'PGInt4)
55− ]
56−
57−sumAndCol1 :: Projection '[] Columns
60+sumAndCol1 :: Projection '[] Columns '[ '("sum", 'PGInt4), '("col1", 'PGInt4)]
58− '[ '("sum", 'PGInt4)
59− , '("col1", 'PGInt4)
60− ]
61sumAndCol1 = project ((#col1 + #col2) `As` #sum :& #col1 :& RNil)
61sumAndCol1 = project ((#col1 + #col2) `As` #sum :& #col1 :& RNil)
62
62
63−table1 :: Relation '[] '[ '("table1", Columns)] Columns
63+table1 :: Relation '[] Tables Columns
64table1 = #table1
64table1 = #table1
65
65
66−parameterizedTable1 :: Relation '[ 'PGInt8] '[ '("table1", Columns)] Columns
66+parameterizedTable1 :: Relation '[ 'PGInt8] Tables Columns
67parameterizedTable1 = #table1
67parameterizedTable1 = #table1
68+
69+insertion :: Insertion '[] Tables Columns
70+insertion = into (Proxy @"table1") (2 `As` #col1 :& 4 `As` #col2 :& RNil)