@@ -3,6 +3,7 @@
4 , FlexibleInstances
4 , FlexibleInstances
5 , FunctionalDependencies
5 , FunctionalDependencies
6 , LambdaCase
7 , LambdaCase
7 , MagicHash
8 , MagicHash
8 , OverloadedStrings
9 , OverloadedStrings
@@ -15,6 +16,7 @@ module Squeel.PostgreSQL.Statement where
16import Data.Boolean
17import Data.Boolean
17import Data.ByteString (ByteString)
18import Data.ByteString (ByteString)
18import Data.Monoid
20import Data.Monoid
19import Data.String
21import Data.String
20import Data.Vinyl
22import Data.Vinyl
@@ -28,9 +30,9 @@ import qualified Data.ByteString as ByteString
28import Squeel.PostgreSQL.Schema
30import Squeel.PostgreSQL.Schema
30newtype Expression
32newtype Expression
31− (params :: [PGType])
33+ (params :: [NullityType])
32− (tables :: [(Symbol,[(Symbol,PGType)])])
34+ (tables :: [(Symbol,[(Symbol,NullityType)])])
33− (ty :: PGType)
35+ (ty :: NullityType)
34 = UnsafeExpression { renderExpression :: ByteString }
36 = UnsafeExpression { renderExpression :: ByteString }
36class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
38class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
@@ -94,7 +96,7 @@ unsafeFunction
94unsafeFunction fn x = UnsafeExpression $ mconcat
96unsafeFunction fn x = UnsafeExpression $ mconcat
95 [fn, "(", renderExpression x, ")"]
97 [fn, "(", renderExpression x, ")"]
97−instance PGNum ty => Num (Expression params tables ty) where
99+instance PGNum ty => Num (Expression params tables (nullity ty)) where
98 (+) = unsafeBinaryOp "+"
100 (+) = unsafeBinaryOp "+"
99 (-) = unsafeBinaryOp "-"
101 (-) = unsafeBinaryOp "-"
100 (*) = unsafeBinaryOp "*"
102 (*) = unsafeBinaryOp "*"
@@ -102,7 +104,7 @@ instance PGNum ty => Num (Expression params tables ty) where
102 signum = unsafeFunction "sign"
104 signum = unsafeFunction "sign"
103 fromInteger = UnsafeExpression . fromString . show
105 fromInteger = UnsafeExpression . fromString . show
105−instance IsString (Expression params tables 'PGText) where
107+instance IsString (Expression params tables (nullity 'PGText)) where
106 fromString str = UnsafeExpression $
108 fromString str = UnsafeExpression $
107 "E\'" <> fromString (escape =<< str) <> "\'"
109 "E\'" <> fromString (escape =<< str) <> "\'"
@@ -117,7 +119,7 @@ instance IsString (Expression params tables 'PGText) where
117 '\\' -> "\\\\"
119 '\\' -> "\\\\"
120−instance Boolean (Expression params tables 'PGBool) where
122+instance Boolean (Expression params tables ('NotNull 'PGBool)) where
121 true = UnsafeExpression "TRUE"
123 true = UnsafeExpression "TRUE"
122 false = UnsafeExpression "FALSE"
124 false = UnsafeExpression "FALSE"
123 notB = unsafeUnaryOp "NOT"
125 notB = unsafeUnaryOp "NOT"
@@ -125,10 +127,21 @@ instance Boolean (Expression params tables 'PGBool) where
125 (||*) = unsafeBinaryOp "OR"
127 (||*) = unsafeBinaryOp "OR"
127type instance BooleanOf (Expression params tables ty) =
129type instance BooleanOf (Expression params tables ty) =
128− Expression params tables 'PGBool
130+ Expression params tables ('NotNull 'PGBool)
133+ :: [Expression params tables ('Null x)]
134+ -> Expression params tables ('NotNull x)
135+ -> Expression params tables ('NotNull x)
136+coalesce nulls notNull = UnsafeExpression $ mconcat
138+ , ByteString.intercalate ", " $ map renderExpression nulls
139+ , ", ", renderExpression notNull
130caseWhenThenElse
143caseWhenThenElse
131− :: [(Expression params tables 'PGBool, Expression params tables ty)]
144+ :: [(Expression params tables ('NotNull 'PGBool), Expression params tables ty)]
132 -> Expression params tables ty
145 -> Expression params tables ty
133 -> Expression params tables ty
146 -> Expression params tables ty
134caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
147caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
@@ -157,45 +170,105 @@ instance OrdB (Expression params tables ty) where
157 (<*) = unsafeBinaryOp "<"
170 (<*) = unsafeBinaryOp "<"
158 (<=*) = unsafeBinaryOp "<="
171 (<=*) = unsafeBinaryOp "<="
160−newtype TableRef
173+newtype TableExpression
161− (params :: [PGType])
174+ (params :: [NullityType])
162− (schema :: [(Symbol,[(Symbol,PGType)])])
175+ (schema :: [(Symbol,[(Symbol,NullityType)])])
163− (tables :: [(Symbol,[(Symbol,PGType)])])
176+ (columns :: [(Symbol,NullityType)])
164− = UnsafeTableRef { renderTableRef :: ByteString }
177+ = UnsafeTableExpression { renderTableExpression :: ByteString }
179+data JoinExpression params schema tables where
181+ :: Aliased (TableExpression params schema) table
182+ -> JoinExpression params schema '[table]
184+ :: Aliased (Selection params schema) table
185+ -> JoinExpression params schema '[table]
187+ :: Aliased (TableExpression params schema) table
188+ -> JoinExpression params schema tables
189+ -> JoinExpression params schema (table ': tables)
191+ :: Aliased (TableExpression params schema) table
192+ -> Expression params (table ': tables) ('NotNull 'PGBool)
193+ -> JoinExpression params schema tables
194+ -> JoinExpression params schema (table ': tables)
196+ :: Aliased (TableExpression params schema) right
197+ -> Expression params '[left,right] ('NotNull 'PGBool)
198+ -> JoinExpression params schema (tables)
199+ -> JoinExpression params schema (NullifyTable right ': left ': tables)
201+ :: Aliased (TableExpression params schema) right
202+ -> Expression params '[left,right] ('NotNull 'PGBool)
203+ -> JoinExpression params schema (left : tables)
204+ -> JoinExpression params schema (right ': NullifyTable left ': tables)
206+ :: Aliased (TableExpression params schema) right
207+ -> Expression params '[left,right] ('NotNull 'PGBool)
208+ -> JoinExpression params schema (left : tables)
209+ -> JoinExpression params schema
210+ (NullifyTable right ': NullifyTable left ': tables)
212+renderJoinExpression :: JoinExpression params schema tables -> ByteString
213+renderJoinExpression = \case
214+ Table table -> renderAliased renderTableExpression table
215+ Subselect selection -> "SELECT " <> renderAliased renderSelection selection
216+ Cross table tables -> mconcat
217+ [ renderJoinExpression tables
219+ , renderAliased renderTableExpression table
221+ Inner table on tables -> mconcat
222+ [ renderJoinExpression tables
224+ , renderAliased renderTableExpression table
226+ , renderExpression on
228+ LeftOuter table on tables -> mconcat
229+ [ renderJoinExpression tables
230+ , " LEFT OUTER JOIN "
231+ , renderAliased renderTableExpression table
233+ , renderExpression on
235+ RightOuter table on tables -> mconcat
236+ [ renderJoinExpression tables
237+ , " RIGHT OUTER JOIN "
238+ , renderAliased renderTableExpression table
240+ , renderExpression on
242+ FullOuter table on tables -> mconcat
243+ [ renderJoinExpression tables
244+ , " FULL OUTER JOIN "
245+ , renderAliased renderTableExpression table
247+ , renderExpression on
166class KnownSymbol table => HasTable table tables columns
250class KnownSymbol table => HasTable table tables columns
167 | table tables -> columns where
251 | table tables -> columns where
168− getTable :: Proxy# table -> TableRef params tables '[table ::: columns]
252+ getTable :: Proxy# table -> TableExpression params tables columns
169− getTable table = UnsafeTableRef $ fromString $ symbolVal' table
253+ getTable table = UnsafeTableExpression $ fromString $ symbolVal' table
170instance {-# OVERLAPPING #-} KnownSymbol table
254instance {-# OVERLAPPING #-} KnownSymbol table
171 => HasTable table ((table ::: columns) ': tables) columns
255 => HasTable table ((table ::: columns) ': tables) columns
172instance {-# OVERLAPPABLE #-}
256instance {-# OVERLAPPABLE #-}
173 (KnownSymbol table, HasTable table schema columns)
257 (KnownSymbol table, HasTable table schema columns)
174 => HasTable table (table' ': schema) columns
258 => HasTable table (table' ': schema) columns
176−crossJoin
260+instance HasTable table schema columns
177− :: TableRef params schema '[table]
261+ => IsLabel table (TableExpression params schema columns) where
178− -> TableRef params schema tables
262+ fromLabel = getTable
179− -> TableRef params schema (table ': tables)
263+
180−crossJoin tab tabs = UnsafeTableRef $
264+instance HasTable table schema columns
181− renderTableRef tabs <> " CROSS JOIN " <> renderTableRef tab
265+ => IsLabel table (JoinExpression params schema '[table ::: columns]) where
182−
266+ fromLabel p = Table $ fromLabel p
184− :: TableRef params schema '[table]
185− -> Expression params (table ': tables) 'PGBool
186− -> TableRef params schema tables
187− -> TableRef params schema (table ': tables)
188−innerJoin tab on tabs = UnsafeTableRef $
190− <> " INNER JOIN " <>
195data Clauses params tables = Clauses
268data Clauses params tables = Clauses
196− { whereClause :: Maybe (Expression params tables 'PGBool)
269+ { whereClause :: Maybe (Expression params tables ('NotNull 'PGBool))
197− , limitClause :: Maybe (Expression params '[] 'PGInt8)
270+ , limitClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
198− , offsetClause :: Maybe (Expression params '[] 'PGInt8)
271+ , offsetClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
201instance Monoid (Clauses params tables) where
274instance Monoid (Clauses params tables) where
@@ -218,84 +291,79 @@ instance Monoid (Clauses params tables) where
218 (Just o1,Just o2) -> Just (o1 + o2)
291 (Just o1,Just o2) -> Just (o1 + o2)
221−data TableExpression
294+data FromExpression
222− (params :: [PGType])
295+ (params :: [NullityType])
223− (schema :: [(Symbol,[(Symbol,PGType)])])
296+ (schema :: [(Symbol,[(Symbol,NullityType)])])
224− (tables :: [(Symbol,[(Symbol,PGType)])])
297+ (tables :: [(Symbol,[(Symbol,NullityType)])])
225− = TableExpression
298+ = FromExpression
226− { tableRef :: TableRef params schema tables
299+ { joinExpression :: JoinExpression params schema tables
227− , clauses :: Clauses params tables
300+ , clauses :: Clauses params tables
231− :: TableRef params schema tables
304+ :: JoinExpression params schema tables
232− -> TableExpression params schema tables
305+ -> FromExpression params schema tables
233−tables tref = TableExpression tref mempty
306+join tref = FromExpression tref mempty
235−renderTabulation :: TableExpression params schema tables -> ByteString
308+renderFromExpression :: FromExpression params schema tables -> ByteString
236−renderTabulation (TableExpression tref (Clauses wh lim off))= mconcat
309+renderFromExpression (FromExpression tref (Clauses wh lim off))= mconcat
237− [ renderTableRef tref
310+ [ renderJoinExpression tref
238 , maybe "" ((" WHERE " <>) . renderExpression) wh
311 , maybe "" ((" WHERE " <>) . renderExpression) wh
239 , maybe "" ((" LIMIT " <>) . renderExpression) lim
312 , maybe "" ((" LIMIT " <>) . renderExpression) lim
240 , maybe "" ((" OFFSET " <>) . renderExpression) off
313 , maybe "" ((" OFFSET " <>) . renderExpression) off
243instance (HasTable table schema columns, table ~ table')
316instance (HasTable table schema columns, table ~ table')
244− => IsLabel table (TableRef params schema '[table' ::: columns]) where
245− fromLabel = getTable
247−instance (HasTable table schema columns, table ~ table')
248− => IsLabel table (TableExpression params schema '[table' ::: columns]) where
317+ => IsLabel table (FromExpression params schema '[table' ::: columns]) where
249− fromLabel p = TableExpression (getTable p) mempty
318+ fromLabel p = FromExpression (fromLabel p) mempty
252− :: Expression params tables 'PGBool
321+ :: Expression params tables ('NotNull 'PGBool)
253− -> TableExpression params schema tables
322+ -> FromExpression params schema tables
254− -> TableExpression params schema tables
323+ -> FromExpression params schema tables
255−where_ wh (TableExpression tabs clauses1) = TableExpression tabs
324+where_ wh (FromExpression tabs clauses1) = FromExpression tabs
256 (clauses1 <> Clauses (Just wh) Nothing Nothing)
325 (clauses1 <> Clauses (Just wh) Nothing Nothing)
259− :: Expression params '[] 'PGInt8
328+ :: Expression params '[] ('NotNull 'PGInt8)
260− -> TableExpression params schema tables
329+ -> FromExpression params schema tables
261− -> TableExpression params schema tables
330+ -> FromExpression params schema tables
262−limit lim (TableExpression tabs clauses1) = TableExpression tabs
331+limit lim (FromExpression tabs clauses1) = FromExpression tabs
263 (clauses1 <> Clauses Nothing (Just lim) Nothing)
332 (clauses1 <> Clauses Nothing (Just lim) Nothing)
266− :: Expression params '[] 'PGInt8
335+ :: Expression params '[] ('NotNull 'PGInt8)
267− -> TableExpression params schema tables
336+ -> FromExpression params schema tables
268− -> TableExpression params schema tables
337+ -> FromExpression params schema tables
269−offset off (TableExpression tabs clauses1) = TableExpression tabs
338+offset off (FromExpression tabs clauses1) = FromExpression tabs
270 (clauses1 <> Clauses Nothing Nothing (Just off))
339 (clauses1 <> Clauses Nothing Nothing (Just off))
272newtype Selection
341newtype Selection
273− (params :: [PGType])
342+ (params :: [NullityType])
274− (schema :: [(Symbol,[(Symbol,PGType)])])
343+ (schema :: [(Symbol,[(Symbol,NullityType)])])
275− (columns :: [(Symbol,PGType)])
344+ (columns :: [(Symbol,NullityType)])
277− { renderSelection :: ByteString }
345+ = UnsafeSelection { renderSelection :: ByteString }
280 :: tables ~ '[table ::: columns]
348 :: tables ~ '[table ::: columns]
281− => TableExpression params schema tables
349+ => FromExpression params schema tables
282 -> Selection params schema columns
350 -> Selection params schema columns
283−starFrom tabs = UnsafeSelection $ "* FROM " <> renderTabulation tabs
351+starFrom tabs = UnsafeSelection $ "* FROM " <> renderFromExpression tabs
285−tableStarFrom
353+dotStarFrom
286 :: HasTable table tables columns
354 :: HasTable table tables columns
287 => Alias table
355 => Alias table
288− -> TableExpression params schema tables
356+ -> FromExpression params schema tables
289 -> Selection params schema columns
357 -> Selection params schema columns
290−Alias tab `tableStarFrom` tabs = UnsafeSelection $
358+Alias tab `dotStarFrom` tabs = UnsafeSelection $
291− fromString (symbolVal' tab) <> ".* FROM" <> renderTabulation tabs
359+ fromString (symbolVal' tab) <> ".* FROM " <> renderFromExpression tabs
294 :: Rec (Aliased (Expression params tables)) columns
362 :: Rec (Aliased (Expression params tables)) columns
295− -> TableExpression params schema tables
363+ -> FromExpression params schema tables
296 -> Selection params schema columns
364 -> Selection params schema columns
297list `from` tabs = UnsafeSelection $
365list `from` tabs = UnsafeSelection $
298− renderList list <> " FROM " <> renderTabulation tabs
366+ renderList list <> " FROM " <> renderFromExpression tabs
300 renderList
368 renderList
301 = ByteString.intercalate ", "
369 = ByteString.intercalate ", "
@@ -303,17 +371,24 @@ list `from` tabs = UnsafeSelection $
303 . rmap (Const . renderAliased renderExpression)
371 . rmap (Const . renderAliased renderExpression)
305newtype Statement
373newtype Statement
306− (params :: [PGType])
374+ (params :: [NullityType])
307− (schema0 :: [(Symbol,[(Symbol,PGType)])])
375+ (schema0 :: [(Symbol,[(Symbol,NullityType)])])
308− (schema1 :: [(Symbol,[(Symbol,PGType)])])
376+ (schema1 :: [(Symbol,[(Symbol,NullityType)])])
309− (columns :: [(Symbol,PGType)])
377+ (columns :: [(Symbol,NullityType)])
310 = UnsafeStatement { renderStatement :: ByteString }
378 = UnsafeStatement { renderStatement :: ByteString }
381+ :: Statement params schema0 schema1 '[]
382+ -> Statement params schema1 schema2 results
383+ -> Statement params schema0 schema2 results
384+statement1 .>>. statement2 = UnsafeStatement $
385+ renderStatement statement1 <> " " <> renderStatement statement2
312newtype PreparedStatement
387newtype PreparedStatement
313− (params :: [PGType])
388+ (params :: [NullityType])
314− (schema0 :: [(Symbol,[(Symbol,PGType)])])
389+ (schema0 :: [(Symbol,[(Symbol,NullityType)])])
315− (schema1 :: [(Symbol,[(Symbol,PGType)])])
390+ (schema1 :: [(Symbol,[(Symbol,NullityType)])])
316− (columns :: [(Symbol,PGType)])
391+ (columns :: [(Symbol,NullityType)])
317 = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
392 = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
@@ -323,10 +398,9 @@ select = UnsafeStatement . ("SELECT " <>) . (<> ";") . renderSelection
325 :: Aliased (Selection params schema) table
400 :: Aliased (Selection params schema) table
326− -> TableExpression params schema '[table]
401+ -> FromExpression params schema '[table]
327−subselect selection = TableExpression
402+subselect selection = FromExpression
328− { tableRef = UnsafeTableRef . ("SELECT " <>) $
403+ { joinExpression = Subselect selection
329− renderAliased renderSelection selection
330 , clauses = mempty
404 , clauses = mempty
@@ -373,3 +447,34 @@ instance {-# OVERLAPPING #-}
373instance {-# OVERLAPPABLE #-}
447instance {-# OVERLAPPABLE #-}
374 (KnownSymbol table, DropTable table schema0 schema1)
448 (KnownSymbol table, DropTable table schema0 schema1)
375 => DropTable table (tabCols ': schema0) (tabCols ': schema1)
449 => DropTable table (tabCols ': schema0) (tabCols ': schema1)
451+set :: g x -> (Maybe `Compose` g) x
454+same :: (Maybe `Compose` g) x
455+same = Compose Nothing
458+ :: HasTable table schema columns
460+ -> Rec (Aliased (Maybe `Compose` Expression params '[table ::: columns])) columns
461+ -> Expression params '[table ::: columns] ('NotNull 'PGBool)
462+ -> Statement params schema schema '[]
463+update (Alias table) columns where' = UnsafeStatement $ mconcat
465+ , fromString $ symbolVal' table
467+ , ByteString.intercalate ", " . catMaybes . recordToList $
468+ rmap (Const . renderSet) columns
469+ , " WHERE ", renderExpression where'
472+ :: Aliased (Maybe `Compose` Expression params tables) column
475+ Compose (Just expression) `As` Alias column -> Just $ mconcat
476+ [ fromString $ symbolVal' column
478+ , renderExpression expression
480+ Compose Nothing `As` _ -> Nothing