@@ -21,9 +21,11 @@ import Data.Monoid
2121import Data.Proxy
2222import Data.String
2323import Data.Vinyl
24+import Data.Vinyl.Functor
2425import GHC.OverloadedLabels
2526import GHC.TypeLits
2627
28+import qualified Data.ByteString as ByteString
2729import qualified Data.ByteString.Char8 as Char8
2830
2931import Squeel.PostgreSQL.Type
@@ -33,7 +35,7 @@ class HasField label xs x where
3335instance {-# OVERLAPPING #-} KnownSymbol label
3436 => HasField label ('(label, x) ': xs) x where
3537 fieldName _ _ _ = Char8.pack (symbolVal (Proxy @label))
36−instance HasField label xs y
38+instance {-# OVERLAPPABLE #-} HasField label xs y
3739 => HasField label (x ': xs) y where
3840 fieldName _ _ _ = fieldName (Proxy @label) (Proxy @xs) (Proxy @y)
3941
@@ -45,6 +47,9 @@ instance IsLabel label (obj x)
4547 => IsLabel label (Aliased obj '(label, x)) where
4648 fromLabel p = fromLabel p `As` fromLabel p
4749
50+aliasName :: forall label. KnownSymbol label => Alias label -> ByteString
51+aliasName _ = Char8.pack (symbolVal (Proxy @label))
52+
4853alias :: Aliased obj '(label, x) -> Alias label
4954alias _ = Alias
5055
@@ -59,6 +64,23 @@ renderAliased
5964renderAliased render (obj `As` label) =
6065 render obj <> " AS " <> Char8.pack (symbolVal label)
6166
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+
6284class AllAliased xs where
6385 renderAllAliased
6486 :: (forall x. obj x -> ByteString)
@@ -232,9 +254,33 @@ subselect selection = Relation
232254 , offsetting = Nothing
233255 }
234256
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+
235276newtype Query ps xss yss zs = UnsafeQuery { renderQuery :: ByteString }
236277newtype PreparedQuery ps xss yss zs =
237278 UnsafePreparedQuery { renderPreparedQuery :: ByteString }
238279
239280select :: Selection ps xss ys -> Query ps xss xss ys
240−select selection = UnsafeQuery $ "SELECT " <> renderSelection selection <> ";"
281+select selection = UnsafeQuery $
282+ "SELECT " <> renderSelection selection <> ";"
283+
284+insert :: Insertion ps xss xs -> Query ps xss xss '[]
285+insert insertion = UnsafeQuery $
286+ "INSERT " <> renderInsertion insertion <> ";"