@@ -1,207 +1,131 @@
1{-# LANGUAGE
1{-# LANGUAGE
3− , DataKinds
2+ DataKinds
5 , FlexibleInstances
3 , FlexibleInstances
7− , KindSignatures
5+ , MultiParamTypeClasses
9 , OverloadedStrings
6 , OverloadedStrings
10− , RankNTypes
7+ , PolyKinds
11 , ScopedTypeVariables
8 , ScopedTypeVariables
12 , TypeApplications
9 , TypeApplications
13− , TypeInType
10+ , TypeFamilies
14 , TypeOperators
11 , TypeOperators
17−{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
19module Squeel.PostgreSQL.Query where
14module Squeel.PostgreSQL.Query where
21−import Data.ByteString (ByteString)
16+import Data.Boolean
17+import Data.ByteString
22import Data.Monoid
18import Data.Monoid
23import Data.Proxy
19import Data.Proxy
24import Data.String
20import Data.String
26−import Data.Vinyl.Functor
27import GHC.TypeLits
21import GHC.TypeLits
29import qualified Data.ByteString.Char8 as Char8
23import qualified Data.ByteString.Char8 as Char8
31import Squeel.PostgreSQL.Type
25import Squeel.PostgreSQL.Type
33−data Expression ps cols x =
27+newtype Expression ps xs y =
34− Expression { unExpression :: ByteString } deriving Show
28+ UnsafeExpression { renderExpression :: ByteString }
36param1 :: Expression (p1:ps) cols p1
30param1 :: Expression (p1:ps) cols p1
37−param1 = Expression "$1"
31+param1 = UnsafeExpression "$1"
39param2 :: Expression (p1:p2:ps) cols p2
32param2 :: Expression (p1:p2:ps) cols p2
40−param2 = Expression "$2"
33+param2 = UnsafeExpression "$2"
42param3 :: Expression (p1:p2:p3:ps) cols p3
34param3 :: Expression (p1:p2:p3:ps) cols p3
43−param3 = Expression "$3"
35+param3 = UnsafeExpression "$3"
45param4 :: Expression (p1:p2:p3:p4:ps) cols p4
36param4 :: Expression (p1:p2:p3:p4:ps) cols p4
46−param4 = Expression "$4"
37+param4 = UnsafeExpression "$4"
48param5 :: Expression (p1:p2:p3:p4:p5:ps) cols p5
38param5 :: Expression (p1:p2:p3:p4:p5:ps) cols p5
49−param5 = Expression "$5"
39+param5 = UnsafeExpression "$5"
51−whenThen :: (Expression ps cols x,Expression ps cols y) -> ByteString
41+class HasField ident xs x where
52−whenThen (Expression when_,Expression then_) = mconcat
42+ fieldName :: Proxy ident -> Proxy xs -> Proxy x -> ByteString
53− [" WHEN ",when_," THEN ",then_,"\n"]
43+instance KnownSymbol name => HasField '[name] ((name ':= x) ': xs) x where
54−
44+ fieldName _ _ _ = Char8.pack (symbolVal (Proxy @name))
55−case_
45+instance HasField ident xs x => HasField ident (y ': xs) x where
56− :: [(Expression ps cols 'PGBool,Expression ps cols x)]
46+ fieldName _ _ _ = fieldName (Proxy @ident) (Proxy @xs) (Proxy @x)
57− -> Expression ps cols x
47+instance (KnownSymbol name, HasField names xs x)
58− -> Expression ps cols x
48+ => HasField (name ': names) ((name ':= xs) ': xss) x where
59−case_ whens (Expression else_) = Expression $ mconcat
49+ fieldName _ _ _ = Char8.pack (symbolVal (Proxy @name))
60− [ "CASE\n"
50+ <> "." <> fieldName (Proxy @names) (Proxy @xs) (Proxy @x)
61− , mconcat (map whenThen whens)
51+
62− , " ELSE ",else_
52+column_
63− , "\nEND"
53+ :: forall ident xs x ps
64− ]
54+ . HasField ident xs x
67− :: Expression ps cols x
68− -> [(Expression ps cols x,Expression ps cols y)]
69− -> Expression ps cols y
56+ -> Expression ps xs x
70− -> Expression ps cols y
57+column_ (_ :: Proxy ident) = UnsafeExpression $
71−caseSwitch (Expression val) whens (Expression else_) = Expression $ mconcat
58+ fieldName (Proxy @ident) (Proxy @xs) (Proxy @x)
72− [ "CASE ",val,"\n"
59+
73− , mconcat (map whenThen whens)
60+data Identified ident x = (:=) ident x
74− , " ELSE ",else_
61+data Aliased obj named where AS :: obj x -> Aliased obj (name ':= x)
76− ]
63+unalias :: Aliased obj (name ':= x) -> obj x
64+unalias (AS obj) = obj
79− :: forall colName colType params columns
67+ :: forall ident xs x ps
80− . Alias (Expression params columns) (colName ':= colType)
68+ . HasField ident xs x
81− -> Expression params columns colType
69+ => Aliased (Expression ps xs) (ident ':= x)
82−column = \case
70+column = AS $ column_ (Proxy @ident)
84− Elem -> Expression $ Char8.pack (symbolVal (Proxy @colName))
86−true :: Expression ps cols 'PGBool
87−true = Expression "true"
89−false :: Expression ps cols 'PGBool
90−false = Expression "false"
93− :: Expression ps cols x
94− -> Expression ps cols x
95− -> Expression ps cols 'PGBool
96−(.==) (Expression x1) (Expression x2) = Expression $ x1 <> "=" <> x2
99− :: Expression ps cols x
100− -> Expression ps cols x
101− -> Expression ps cols 'PGBool
102−(./=) (Expression x1) (Expression x2) = Expression $ x1 <> "<>" <> x2
105− :: Expression ps cols x
106− -> Expression ps cols x
107− -> Expression ps cols 'PGBool
108−(.>) (Expression x1) (Expression x2) = Expression $ x1 <> ">" <> x2
111− :: Expression ps cols x
112− -> Expression ps cols x
113− -> Expression ps cols 'PGBool
114−(.<) (Expression x1) (Expression x2) = Expression $ x1 <> "<" <> x2
117− :: Expression ps cols x
118− -> Expression ps cols x
119− -> Expression ps cols 'PGBool
120−(.>=) (Expression x1) (Expression x2) = Expression $ x1 <> ">=" <> x2
123− :: Expression ps cols x
124− -> Expression ps cols x
125− -> Expression ps cols 'PGBool
126−(.<=) (Expression x1) (Expression x2) = Expression $ x1 <> "<=" <> x2
128−instance IsString (Expression ps cols 'PGText) where
72+instance IsString (Expression ps xs 'PGText) where
129− fromString str = Expression $ "\'" <> fromString str <> "\'"
73+ fromString str = UnsafeExpression $ "\'" <> fromString str <> "\'"
131−instance Num (Expression ps cols 'PGInt4) where
75+instance Num (Expression ps xs 'PGInt4) where
132− fromInteger n = Expression . fromString $ show n
76+ fromInteger n = UnsafeExpression . fromString $ show n
133− Expression x + Expression y = Expression $ x <> "+" <> y
77+ x + y = UnsafeExpression $ renderExpression x <> "+" <> renderExpression y
134− Expression x * Expression y = Expression $ x <> "*" <> y
78+ x * y = UnsafeExpression $ renderExpression x <> "*" <> renderExpression y
135− Expression x - Expression y = Expression $ x <> "+" <> y
79+ UnsafeExpression x - UnsafeExpression y = UnsafeExpression $ x <> "+" <> y
136− abs (Expression x) = Expression $ "@" <> x
80+ abs (UnsafeExpression x) = UnsafeExpression $ "@" <> x
137− signum (Expression x) = Expression $ "sign(" <> x <> ")"
81+ signum (UnsafeExpression x) = UnsafeExpression $ "sign(" <> x <> ")"
83+instance Boolean (Expression ps xs 'PGBool) where
84+ true = UnsafeExpression "TRUE"
85+ false = UnsafeExpression "FALSE"
86+ notB x = UnsafeExpression $ "NOT " <> renderExpression x
87+ x &&* y = UnsafeExpression $
88+ renderExpression x <> " AND " <> renderExpression y
89+ x ||* y = UnsafeExpression $
90+ renderExpression x <> " OR " <> renderExpression y
92+type instance BooleanOf (Expression ps xs y) = Expression ps xs 'PGBool
94+instance IfB (Expression ps xs y) where
95+ ifB predicate then_ else_ = UnsafeExpression $ mconcat
97+ , "\n WHEN ",renderExpression predicate
98+ , "\n THEN ",renderExpression then_
99+ , "\n ELSE ",renderExpression else_
103+instance EqB (Expression ps xs y) where
104+ x ==* y = UnsafeExpression $ renderExpression x <> "=" <> renderExpression y
105+ x /=* y = UnsafeExpression $ renderExpression x <> "<>" <> renderExpression y
107+instance OrdB (Expression ps xs y) where
108+ x >* y = UnsafeExpression $ renderExpression x <> ">" <> renderExpression y
109+ x >=* y = UnsafeExpression $ renderExpression x <> ">=" <> renderExpression y
110+ x <* y = UnsafeExpression $ renderExpression x <> "<" <> renderExpression y
111+ x <=* y = UnsafeExpression $ renderExpression x <> "<=" <> renderExpression y
139−data Relation ps xs ys = Relation { unRelation :: ByteString } deriving Show
113+data Relation ps xs ys = UnsafeRelation { renderRelation :: ByteString }
141−star :: Relation ps xs xs
115+table_
142−star = Relation "*"
116+ :: forall ident xss xs ps
143−
117+ . HasField ident xss xs
144−project :: Rec (Alias (Expression ps xs)) ys -> Relation ps xs ys
119+ -> Relation ps xss xs
145−project
120+table_ (_ :: Proxy ident) = UnsafeRelation $
146− = Relation
121+ fieldName (Proxy @ident) (Proxy @xss) (Proxy @xs)
147− . Char8.intercalate ", "
122+
148− . recordToList
123+table
149− . rmap (Const . expressionString)
124+ :: forall ident xss xs ps
150− where
125+ . HasField ident xss xs
151− expressionString :: Alias (Expression ps xs) y -> ByteString
126+ => Aliased (Relation ps xss) (ident ':= xs)
152− expressionString = \case
127+table = AS $ table_ (Proxy @ident)
153− (As (Expression expr) :: Alias (Expression ps xs) y)
128+
154− -> expr <> " AS " <> Char8.pack (symbolVal (Proxy @(Name y)))
129+newtype Query ps xss yss zs = UnsafeQuery { renderQuery :: ByteString }
155− (Elem :: Alias (Expression ps xs) y)
130+newtype PreparedQuery ps xss yss zs =
156− -> Char8.pack (symbolVal (Proxy @(Name y)))
131+ UnsafePreparedQuery { renderPreparedQuery :: ByteString }
158−-- star :: Relation db params xs xs
159−-- star = Relation "SELECT * FROM "
162−-- :: forall tableName tableCols db params.
163−-- ( KnownSymbol tableName
164−-- , (tableName ':= tableCols) `Elem` Unnamed db ~ 'True
165−-- ) => Proxy tableName -> Relation db params '[] tableCols
166−-- table (_ :: Proxy tableName) = Relation $
167−-- "SELECT * FROM " <> Char8.pack (symbolVal (Proxy @tableName))
170−-- :: forall tableName tableCols db params columns
171−-- . Alias (Relation params columns db) (tableName ':= tableCols)
172−-- -> Relation params columns db tableCols
175−-- Elem -> Relation $
176−-- "SELECT * FROM " <> Char8.pack (symbolVal (Proxy @tableName))
179−-- :: Relation db params ys zs
180−-- -> Relation db params xs ys
181−-- -> Relation db params xs zs
182−-- from (Relation rel1) (Relation rel2) = Relation $ rel1 <> rel2
184−-- project :: Rec (Alias (Expression ps xs)) ys -> Relation db params xs ys
185−-- project exprs = Relation $
186−-- "SELECT " <> Char8.intercalate "," (exprStrings exprs) <> " FROM "
188−-- exprStrings :: Rec (Alias (Expression ps xs)) ys -> [ByteString]
189−-- exprStrings = \case
191−-- (As (Expression expr) :: Alias (Expression _ _) (exprName ':= _))
193−-- (expr <> " As " <> Char8.pack (symbolVal (Proxy @exprName)))
194−-- : expressionStrings exprs
201− = Query { unQuery :: ByteString }
203−newtype PreparedQuery db0 db1 ps xs
204− = PreparedQuery { unPreparedQuery :: ByteString }
206−-- select :: Relation ps ('[] :: [Column]) db xs -> Query db db ps xs
207−-- select (Relation x) = Query x