on dev help sign in
Merge pull request #19 from echatav/self-join

Updates, Nulls and Joins

commit871b2a2c9a439ae321a7c930add4885fc37fd2a6 authorEitan Chatav <eitan.chatav@gmail.com> dateFri 16 Jun 2017, 11:45 (9 years ago) parent230810fd23d95c
4 files changed+289−135
squeel-postgresql/src/Squeel/PostgreSQL/PQ.hs+2−2 squeel-postgresql/src/Squeel/PostgreSQL/Schema.hs+44−4 squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+199−94 squeel-postgresql/test/Squeel/PostgreSQL/StatementSpec.hs+44−35
squeel-postgresql/src/Squeel/PostgreSQL/PQ.hs+2−2
@@ -35,8 +35,8 @@ import Squeel.PostgreSQL.Schema
3535newtype Connection db = Connection { unConnection :: LibPQ.Connection }
3636
3737newtype PQ
38− (db0 :: [(Symbol,[(Symbol,PGType)])])
39− (db1 :: [(Symbol,[(Symbol,PGType)])])
38+ (db0 :: [(Symbol,[(Symbol,NullityType)])])
39+ (db1 :: [(Symbol,[(Symbol,NullityType)])])
4040 (m :: * -> *)
4141 (x :: *) =
4242 PQ { runPQ :: Connection db0 -> m (x, Connection db1) }
squeel-postgresql/src/Squeel/PostgreSQL/Schema.hs+44−4
@@ -9,6 +9,7 @@
99 , PolyKinds
1010 , RankNTypes
1111 , ScopedTypeVariables
12+ , TypeFamilies
1213 , TypeOperators
1314#-}
1415
@@ -23,6 +24,10 @@ module Squeel.PostgreSQL.Schema
2324 , KnownPGType (..)
2425 , KnownColumns (..)
2526 , renderAliased
27+ , NullityType (..)
28+ , NullifyType
29+ , NullifyColumns
30+ , NullifyTable
2631 , module GHC.OverloadedLabels
2732 , module GHC.TypeLits
2833 ) where
@@ -134,24 +139,59 @@ instance KnownPGType 'PGUuid where renderPGType _ = "uuid"
134139instance KnownPGType 'PGJson where renderPGType _ = "json"
135140instance KnownPGType 'PGJsonb where renderPGType _ = "jsonb"
136141
137−class KnownColumns (columns :: [(Symbol,PGType)]) where
142+class KnownColumns (columns :: [(Symbol,NullityType)]) where
138143 renderColumns :: Proxy# columns -> ByteString
139144instance
140145 ( KnownSymbol column
141146 , KnownPGType ty
142− ) => KnownColumns '[column ::: ty] where
147+ ) => KnownColumns '[column ::: 'NotNull ty] where
143148 renderColumns _ =
144149 fromString (symbolVal' (proxy# :: Proxy# column))
145150 <> " "
146151 <> renderPGType (proxy# :: Proxy# ty)
152+ <> " NOT NULL"
153+instance
154+ ( KnownSymbol column
155+ , KnownPGType ty
156+ ) => KnownColumns '[column ::: 'Null ty] where
157+ renderColumns _ =
158+ fromString (symbolVal' (proxy# :: Proxy# column))
159+ <> " "
160+ <> renderPGType (proxy# :: Proxy# ty)
161+ <> " NULL"
162+instance
163+ ( KnownSymbol column
164+ , KnownPGType ty
165+ , KnownColumns (columnsHead ': columnsTail)
166+ ) => KnownColumns ((column ::: 'NotNull ty) ': columnsHead ': columnsTail) where
167+ renderColumns _ =
168+ fromString (symbolVal' (proxy# :: Proxy# column))
169+ <> " "
170+ <> renderPGType (proxy# :: Proxy# ty)
171+ <> " NOT NULL, "
172+ <> renderColumns (proxy# :: Proxy# (columnsHead ': columnsTail))
147173instance
148174 ( KnownSymbol column
149175 , KnownPGType ty
150176 , KnownColumns (columnsHead ': columnsTail)
151− ) => KnownColumns ((column ::: ty) ': columnsHead ': columnsTail) where
177+ ) => KnownColumns ((column ::: 'Null ty) ': columnsHead ': columnsTail) where
152178 renderColumns _ =
153179 fromString (symbolVal' (proxy# :: Proxy# column))
154180 <> " "
155181 <> renderPGType (proxy# :: Proxy# ty)
156− <> ", "
182+ <> " NULL, "
157183 <> renderColumns (proxy# :: Proxy# (columnsHead ': columnsTail))
184+
185+data NullityType = Null PGType | NotNull PGType
186+
187+type family NullifyType nullty where
188+ NullifyType ('Null ty) = 'Null ty
189+ NullifyType ('NotNull ty) = 'Null ty
190+
191+type family NullifyColumns columns where
192+ NullifyColumns '[] = '[]
193+ NullifyColumns ((column ::: ty) ': columns) =
194+ (column ::: (NullifyType ty)) ': (NullifyColumns columns)
195+
196+type family NullifyTable table where
197+ NullifyTable (table ::: columns) = table ::: NullifyColumns columns
squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+199−94
@@ -3,6 +3,7 @@
33 DataKinds
44 , FlexibleInstances
55 , FunctionalDependencies
6+ , GADTs
67 , LambdaCase
78 , MagicHash
89 , OverloadedStrings
@@ -15,6 +16,7 @@ module Squeel.PostgreSQL.Statement where
1516
1617import Data.Boolean
1718import Data.ByteString (ByteString)
19+import Data.Maybe
1820import Data.Monoid
1921import Data.String
2022import Data.Vinyl
@@ -28,9 +30,9 @@ import qualified Data.ByteString as ByteString
2830import Squeel.PostgreSQL.Schema
2931
3032newtype Expression
31− (params :: [PGType])
32− (tables :: [(Symbol,[(Symbol,PGType)])])
33− (ty :: PGType)
33+ (params :: [NullityType])
34+ (tables :: [(Symbol,[(Symbol,NullityType)])])
35+ (ty :: NullityType)
3436 = UnsafeExpression { renderExpression :: ByteString }
3537
3638class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
@@ -94,7 +96,7 @@ unsafeFunction
9496unsafeFunction fn x = UnsafeExpression $ mconcat
9597 [fn, "(", renderExpression x, ")"]
9698
97−instance PGNum ty => Num (Expression params tables ty) where
99+instance PGNum ty => Num (Expression params tables (nullity ty)) where
98100 (+) = unsafeBinaryOp "+"
99101 (-) = unsafeBinaryOp "-"
100102 (*) = unsafeBinaryOp "*"
@@ -102,7 +104,7 @@ instance PGNum ty => Num (Expression params tables ty) where
102104 signum = unsafeFunction "sign"
103105 fromInteger = UnsafeExpression . fromString . show
104106
105−instance IsString (Expression params tables 'PGText) where
107+instance IsString (Expression params tables (nullity 'PGText)) where
106108 fromString str = UnsafeExpression $
107109 "E\'" <> fromString (escape =<< str) <> "\'"
108110 where
@@ -117,7 +119,7 @@ instance IsString (Expression params tables 'PGText) where
117119 '\\' -> "\\\\"
118120 c -> [c]
119121
120−instance Boolean (Expression params tables 'PGBool) where
122+instance Boolean (Expression params tables ('NotNull 'PGBool)) where
121123 true = UnsafeExpression "TRUE"
122124 false = UnsafeExpression "FALSE"
123125 notB = unsafeUnaryOp "NOT"
@@ -125,10 +127,21 @@ instance Boolean (Expression params tables 'PGBool) where
125127 (||*) = unsafeBinaryOp "OR"
126128
127129type instance BooleanOf (Expression params tables ty) =
128− Expression params tables 'PGBool
130+ Expression params tables ('NotNull 'PGBool)
131+
132+coalesce
133+ :: [Expression params tables ('Null x)]
134+ -> Expression params tables ('NotNull x)
135+ -> Expression params tables ('NotNull x)
136+coalesce nulls notNull = UnsafeExpression $ mconcat
137+ [ "COALESCE("
138+ , ByteString.intercalate ", " $ map renderExpression nulls
139+ , ", ", renderExpression notNull
140+ , ")"
141+ ]
129142
130143caseWhenThenElse
131− :: [(Expression params tables 'PGBool, Expression params tables ty)]
144+ :: [(Expression params tables ('NotNull 'PGBool), Expression params tables ty)]
132145 -> Expression params tables ty
133146 -> Expression params tables ty
134147caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
@@ -157,45 +170,105 @@ instance OrdB (Expression params tables ty) where
157170 (<*) = unsafeBinaryOp "<"
158171 (<=*) = unsafeBinaryOp "<="
159172
160−newtype TableRef
161− (params :: [PGType])
162− (schema :: [(Symbol,[(Symbol,PGType)])])
163− (tables :: [(Symbol,[(Symbol,PGType)])])
164− = UnsafeTableRef { renderTableRef :: ByteString }
173+newtype TableExpression
174+ (params :: [NullityType])
175+ (schema :: [(Symbol,[(Symbol,NullityType)])])
176+ (columns :: [(Symbol,NullityType)])
177+ = UnsafeTableExpression { renderTableExpression :: ByteString }
178+
179+data JoinExpression params schema tables where
180+ Table
181+ :: Aliased (TableExpression params schema) table
182+ -> JoinExpression params schema '[table]
183+ Subselect
184+ :: Aliased (Selection params schema) table
185+ -> JoinExpression params schema '[table]
186+ Cross
187+ :: Aliased (TableExpression params schema) table
188+ -> JoinExpression params schema tables
189+ -> JoinExpression params schema (table ': tables)
190+ Inner
191+ :: Aliased (TableExpression params schema) table
192+ -> Expression params (table ': tables) ('NotNull 'PGBool)
193+ -> JoinExpression params schema tables
194+ -> JoinExpression params schema (table ': tables)
195+ LeftOuter
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)
200+ RightOuter
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)
205+ FullOuter
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)
211+
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
218+ , " CROSS JOIN "
219+ , renderAliased renderTableExpression table
220+ ]
221+ Inner table on tables -> mconcat
222+ [ renderJoinExpression tables
223+ , " INNER JOIN "
224+ , renderAliased renderTableExpression table
225+ , " ON "
226+ , renderExpression on
227+ ]
228+ LeftOuter table on tables -> mconcat
229+ [ renderJoinExpression tables
230+ , " LEFT OUTER JOIN "
231+ , renderAliased renderTableExpression table
232+ , " ON "
233+ , renderExpression on
234+ ]
235+ RightOuter table on tables -> mconcat
236+ [ renderJoinExpression tables
237+ , " RIGHT OUTER JOIN "
238+ , renderAliased renderTableExpression table
239+ , " ON "
240+ , renderExpression on
241+ ]
242+ FullOuter table on tables -> mconcat
243+ [ renderJoinExpression tables
244+ , " FULL OUTER JOIN "
245+ , renderAliased renderTableExpression table
246+ , " ON "
247+ , renderExpression on
248+ ]
165249
166250class KnownSymbol table => HasTable table tables columns
167251 | table tables -> columns where
168− getTable :: Proxy# table -> TableRef params tables '[table ::: columns]
169− getTable table = UnsafeTableRef $ fromString $ symbolVal' table
252+ getTable :: Proxy# table -> TableExpression params tables columns
253+ getTable table = UnsafeTableExpression $ fromString $ symbolVal' table
170254instance {-# OVERLAPPING #-} KnownSymbol table
171255 => HasTable table ((table ::: columns) ': tables) columns
172256instance {-# OVERLAPPABLE #-}
173257 (KnownSymbol table, HasTable table schema columns)
174258 => HasTable table (table' ': schema) columns
175259
176−crossJoin
177− :: TableRef params schema '[table]
178− -> TableRef params schema tables
179− -> TableRef params schema (table ': tables)
180−crossJoin tab tabs = UnsafeTableRef $
181− renderTableRef tabs <> " CROSS JOIN " <> renderTableRef tab
182−
183−innerJoin
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 $
189− renderTableRef tabs
190− <> " INNER JOIN " <>
191− renderTableRef tab
192− <> " ON " <>
193− renderExpression on
260+instance HasTable table schema columns
261+ => IsLabel table (TableExpression params schema columns) where
262+ fromLabel = getTable
263+
264+instance HasTable table schema columns
265+ => IsLabel table (JoinExpression params schema '[table ::: columns]) where
266+ fromLabel p = Table $ fromLabel p
194267
195268data Clauses params tables = Clauses
196− { whereClause :: Maybe (Expression params tables 'PGBool)
197− , limitClause :: Maybe (Expression params '[] 'PGInt8)
198− , offsetClause :: Maybe (Expression params '[] 'PGInt8)
269+ { whereClause :: Maybe (Expression params tables ('NotNull 'PGBool))
270+ , limitClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
271+ , offsetClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
199272 }
200273
201274instance Monoid (Clauses params tables) where
@@ -218,84 +291,79 @@ instance Monoid (Clauses params tables) where
218291 (Just o1,Just o2) -> Just (o1 + o2)
219292 }
220293
221−data TableExpression
222− (params :: [PGType])
223− (schema :: [(Symbol,[(Symbol,PGType)])])
224− (tables :: [(Symbol,[(Symbol,PGType)])])
225− = TableExpression
226− { tableRef :: TableRef params schema tables
227− , clauses :: Clauses params tables
228− }
294+data FromExpression
295+ (params :: [NullityType])
296+ (schema :: [(Symbol,[(Symbol,NullityType)])])
297+ (tables :: [(Symbol,[(Symbol,NullityType)])])
298+ = FromExpression
299+ { joinExpression :: JoinExpression params schema tables
300+ , clauses :: Clauses params tables
301+ }
229302
230−tables
231− :: TableRef params schema tables
232− -> TableExpression params schema tables
233−tables tref = TableExpression tref mempty
303+join
304+ :: JoinExpression params schema tables
305+ -> FromExpression params schema tables
306+join tref = FromExpression tref mempty
234307
235−renderTabulation :: TableExpression params schema tables -> ByteString
236−renderTabulation (TableExpression tref (Clauses wh lim off))= mconcat
237− [ renderTableRef tref
308+renderFromExpression :: FromExpression params schema tables -> ByteString
309+renderFromExpression (FromExpression tref (Clauses wh lim off))= mconcat
310+ [ renderJoinExpression tref
238311 , maybe "" ((" WHERE " <>) . renderExpression) wh
239312 , maybe "" ((" LIMIT " <>) . renderExpression) lim
240313 , maybe "" ((" OFFSET " <>) . renderExpression) off
241314 ]
242315
243316instance (HasTable table schema columns, table ~ table')
244− => IsLabel table (TableRef params schema '[table' ::: columns]) where
245− fromLabel = getTable
246−
247−instance (HasTable table schema columns, table ~ table')
248− => IsLabel table (TableExpression params schema '[table' ::: columns]) where
249− fromLabel p = TableExpression (getTable p) mempty
317+ => IsLabel table (FromExpression params schema '[table' ::: columns]) where
318+ fromLabel p = FromExpression (fromLabel p) mempty
250319
251320where_
252− :: Expression params tables 'PGBool
253− -> TableExpression params schema tables
254− -> TableExpression params schema tables
255−where_ wh (TableExpression tabs clauses1) = TableExpression tabs
321+ :: Expression params tables ('NotNull 'PGBool)
322+ -> FromExpression params schema tables
323+ -> FromExpression params schema tables
324+where_ wh (FromExpression tabs clauses1) = FromExpression tabs
256325 (clauses1 <> Clauses (Just wh) Nothing Nothing)
257326
258327limit
259− :: Expression params '[] 'PGInt8
260− -> TableExpression params schema tables
261− -> TableExpression params schema tables
262−limit lim (TableExpression tabs clauses1) = TableExpression tabs
328+ :: Expression params '[] ('NotNull 'PGInt8)
329+ -> FromExpression params schema tables
330+ -> FromExpression params schema tables
331+limit lim (FromExpression tabs clauses1) = FromExpression tabs
263332 (clauses1 <> Clauses Nothing (Just lim) Nothing)
264333
265334offset
266− :: Expression params '[] 'PGInt8
267− -> TableExpression params schema tables
268− -> TableExpression params schema tables
269−offset off (TableExpression tabs clauses1) = TableExpression tabs
335+ :: Expression params '[] ('NotNull 'PGInt8)
336+ -> FromExpression params schema tables
337+ -> FromExpression params schema tables
338+offset off (FromExpression tabs clauses1) = FromExpression tabs
270339 (clauses1 <> Clauses Nothing Nothing (Just off))
271340
272341newtype Selection
273− (params :: [PGType])
274− (schema :: [(Symbol,[(Symbol,PGType)])])
275− (columns :: [(Symbol,PGType)])
276− = UnsafeSelection
277− { renderSelection :: ByteString }
342+ (params :: [NullityType])
343+ (schema :: [(Symbol,[(Symbol,NullityType)])])
344+ (columns :: [(Symbol,NullityType)])
345+ = UnsafeSelection { renderSelection :: ByteString }
278346
279347starFrom
280348 :: tables ~ '[table ::: columns]
281− => TableExpression params schema tables
349+ => FromExpression params schema tables
282350 -> Selection params schema columns
283−starFrom tabs = UnsafeSelection $ "* FROM " <> renderTabulation tabs
351+starFrom tabs = UnsafeSelection $ "* FROM " <> renderFromExpression tabs
284352
285−tableStarFrom
353+dotStarFrom
286354 :: HasTable table tables columns
287355 => Alias table
288− -> TableExpression params schema tables
356+ -> FromExpression params schema tables
289357 -> Selection params schema columns
290−Alias tab `tableStarFrom` tabs = UnsafeSelection $
291− fromString (symbolVal' tab) <> ".* FROM" <> renderTabulation tabs
358+Alias tab `dotStarFrom` tabs = UnsafeSelection $
359+ fromString (symbolVal' tab) <> ".* FROM " <> renderFromExpression tabs
292360
293361from
294362 :: Rec (Aliased (Expression params tables)) columns
295− -> TableExpression params schema tables
363+ -> FromExpression params schema tables
296364 -> Selection params schema columns
297365list `from` tabs = UnsafeSelection $
298− renderList list <> " FROM " <> renderTabulation tabs
366+ renderList list <> " FROM " <> renderFromExpression tabs
299367 where
300368 renderList
301369 = ByteString.intercalate ", "
@@ -303,17 +371,24 @@ list `from` tabs = UnsafeSelection $
303371 . rmap (Const . renderAliased renderExpression)
304372
305373newtype Statement
306− (params :: [PGType])
307− (schema0 :: [(Symbol,[(Symbol,PGType)])])
308− (schema1 :: [(Symbol,[(Symbol,PGType)])])
309− (columns :: [(Symbol,PGType)])
374+ (params :: [NullityType])
375+ (schema0 :: [(Symbol,[(Symbol,NullityType)])])
376+ (schema1 :: [(Symbol,[(Symbol,NullityType)])])
377+ (columns :: [(Symbol,NullityType)])
310378 = UnsafeStatement { renderStatement :: ByteString }
311379
380+(.>>.)
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
386+
312387newtype PreparedStatement
313− (params :: [PGType])
314− (schema0 :: [(Symbol,[(Symbol,PGType)])])
315− (schema1 :: [(Symbol,[(Symbol,PGType)])])
316− (columns :: [(Symbol,PGType)])
388+ (params :: [NullityType])
389+ (schema0 :: [(Symbol,[(Symbol,NullityType)])])
390+ (schema1 :: [(Symbol,[(Symbol,NullityType)])])
391+ (columns :: [(Symbol,NullityType)])
317392 = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
318393
319394select
@@ -323,10 +398,9 @@ select = UnsafeStatement . ("SELECT " <>) . (<> ";") . renderSelection
323398
324399subselect
325400 :: Aliased (Selection params schema) table
326− -> TableExpression params schema '[table]
327−subselect selection = TableExpression
328− { tableRef = UnsafeTableRef . ("SELECT " <>) $
329− renderAliased renderSelection selection
401+ -> FromExpression params schema '[table]
402+subselect selection = FromExpression
403+ { joinExpression = Subselect selection
330404 , clauses = mempty
331405 }
332406
@@ -373,3 +447,34 @@ instance {-# OVERLAPPING #-}
373447instance {-# OVERLAPPABLE #-}
374448 (KnownSymbol table, DropTable table schema0 schema1)
375449 => DropTable table (tabCols ': schema0) (tabCols ': schema1)
450+
451+set :: g x -> (Maybe `Compose` g) x
452+set = Compose . Just
453+
454+same :: (Maybe `Compose` g) x
455+same = Compose Nothing
456+
457+update
458+ :: HasTable table schema columns
459+ => Alias table
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
464+ [ "UPDATE "
465+ , fromString $ symbolVal' table
466+ , " SET "
467+ , ByteString.intercalate ", " . catMaybes . recordToList $
468+ rmap (Const . renderSet) columns
469+ , " WHERE ", renderExpression where'
470+ ] where
471+ renderSet
472+ :: Aliased (Maybe `Compose` Expression params tables) column
473+ -> Maybe ByteString
474+ renderSet = \case
475+ Compose (Just expression) `As` Alias column -> Just $ mconcat
476+ [ fromString $ symbolVal' column
477+ , " = "
478+ , renderExpression expression
479+ ]
480+ Compose Nothing `As` _ -> Nothing
squeel-postgresql/test/Squeel/PostgreSQL/StatementSpec.hs+44−35
@@ -28,7 +28,7 @@ spec = do
2828 ((#col1 + #col2) `As` #sum :& #col1 :& RNil)
2929 `from` (#table1 & where_ true)
3030 statement `shouldRenderAs`
31− "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 WHERE TRUE;"
31+ "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE TRUE;"
3232 it "combines WHEREs using AND" $ do
3333 let
3434 statement :: Statement '[] Tables Tables SumAndCol1
@@ -36,40 +36,40 @@ spec = do
3636 ((#col1 + #col2) `As` #sum :& #col1 :& RNil)
3737 `from` (#table1 & where_ true & where_ false)
3838 statement `shouldRenderAs`
39− "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 WHERE (TRUE AND FALSE);"
39+ "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE (TRUE AND FALSE);"
4040 it "performs sub SELECTs" $ do
4141 let
4242 selection = ((#col1 + #col2) `As` #sum :& #col1 :& RNil) `from` #table1
4343 statement :: Statement '[] Tables Tables SumAndCol1
4444 statement = select $ starFrom (subselect (selection `As` #sub))
4545 statement `shouldRenderAs`
46− "SELECT * FROM SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS sub;"
46+ "SELECT * FROM SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 AS sub;"
4747 it "does LIMIT clauses" $ do
4848 let
4949 statement :: Statement '[] Tables Tables Columns
5050 statement = select $ starFrom (#table1 & limit 1)
51− statement `shouldRenderAs` "SELECT * FROM table1 LIMIT 1;"
51+ statement `shouldRenderAs` "SELECT * FROM table1 AS table1 LIMIT 1;"
5252 it "should use the minimum of given LIMITs" $ do
5353 let
5454 statement :: Statement '[] Tables Tables Columns
5555 statement = select $ starFrom (#table1 & limit 1 & limit 2)
5656 statement `shouldRenderAs`
57− "SELECT * FROM table1 LIMIT CASE WHEN (1 <= 2) THEN 1 ELSE 2 END;"
57+ "SELECT * FROM table1 AS table1 LIMIT CASE WHEN (1 <= 2) THEN 1 ELSE 2 END;"
5858 it "should render parameters using $ signs" $ do
5959 let
60− statement :: Statement '[ 'PGInt8] Tables Tables Columns
60+ statement :: Statement '[ 'NotNull 'PGInt8] Tables Tables Columns
6161 statement = select $ starFrom (#table1 & limit param1)
62− statement `shouldRenderAs` "SELECT * FROM table1 LIMIT $1;"
62+ statement `shouldRenderAs` "SELECT * FROM table1 AS table1 LIMIT $1;"
6363 it "does OFFSET clauses" $ do
6464 let
6565 statement :: Statement '[] Tables Tables Columns
6666 statement = select $ starFrom (#table1 & offset 1)
67− statement `shouldRenderAs` "SELECT * FROM table1 OFFSET 1;"
67+ statement `shouldRenderAs` "SELECT * FROM table1 AS table1 OFFSET 1;"
6868 it "should use the sum of given OFFSETs" $ do
6969 let
7070 statement :: Statement '[] Tables Tables Columns
7171 statement = select $ starFrom (#table1 & offset 1 & offset 2)
72− statement `shouldRenderAs` "SELECT * FROM table1 OFFSET (1 + 2);"
72+ statement `shouldRenderAs` "SELECT * FROM table1 AS table1 OFFSET (1 + 2);"
7373 it "correctly render simple INSERTs" $ do
7474 let
7575 statement :: Statement '[] Tables Tables '[]
@@ -93,23 +93,23 @@ spec = do
9393 let
9494 statement :: Statement '[] JoinTables JoinTables ValueColumns
9595 statement = select $ vals `from`
96− (tables (#orders & crossJoin #customers & crossJoin #shippers))
96+ (join (#orders & Cross #customers & Cross #shippers))
9797 statement `shouldRenderAs`
9898 "SELECT\
9999 \ orders.orderVal AS orderVal,\
100100 \ customers.customerVal AS customerVal,\
101101 \ shippers.shipperVal AS shipperVal\
102− \ FROM orders\
103− \ CROSS JOIN customers\
104− \ CROSS JOIN shippers;"
102+ \ FROM orders AS orders\
103+ \ CROSS JOIN customers AS customers\
104+ \ CROSS JOIN shippers AS shippers;"
105105 it "should render INNER JOINs" $ do
106106 let
107− innerJoins :: TableExpression '[] JoinTables JoinTables
108− innerJoins = tables $
107+ innerJoins :: FromExpression '[] JoinTables JoinTables
108+ innerJoins = join $
109109 #orders
110− & innerJoin #customers
110+ & Inner #customers
111111 (#orders .&. #customerID ==* #customers .&. #customerID)
112− & innerJoin #shippers
112+ & Inner #shippers
113113 (#orders .&. #shipperID ==* #shippers .&. #shipperID)
114114 selection :: Statement '[] JoinTables JoinTables ValueColumns
115115 selection = select $ vals `from` innerJoins
@@ -118,46 +118,55 @@ spec = do
118118 \ orders.orderVal AS orderVal,\
119119 \ customers.customerVal AS customerVal,\
120120 \ shippers.shipperVal AS shipperVal\
121− \ FROM orders\
122− \ INNER JOIN customers\
121+ \ FROM orders AS orders\
122+ \ INNER JOIN customers AS customers\
123123 \ ON (orders.customerID = customers.customerID)\
124− \ INNER JOIN shippers\
124+ \ INNER JOIN shippers AS shippers\
125125 \ ON (orders.shipperID = shippers.shipperID);"
126+ it "should render self JOINs" $ do
127+ let
128+ statement :: Statement '[] JoinTables JoinTables OrderColumns
129+ statement = select $ #orders1 `dotStarFrom`
130+ (join (Table (#orders `As` #orders1)
131+ & Cross (#orders `As` #orders2)))
132+ statement `shouldRenderAs`
133+ "SELECT orders1.*\
134+ \ FROM orders AS orders1\
135+ \ CROSS JOIN orders AS orders2;"
126136 it "should render simple CREATE TABLE statements" $ do
127137 let
128138 statement :: Statement '[] '[] Tables '[]
129139 statement = createTable #table1 (proxy# :: Proxy# Columns)
130140 statement `shouldRenderAs`
131− "CREATE TABLE table1 (col1 int4, col2 int4);"
141+ "CREATE TABLE table1 (col1 int4 NOT NULL, col2 int4 NOT NULL);"
132142 it "should render DROP TABLE statements" $ do
133143 let
134144 statement :: Statement '[] Tables '[] '[]
135145 statement = dropTable #table1
136− statement `shouldRenderAs`
137− "DROP TABLE table1;"
146+ statement `shouldRenderAs` "DROP TABLE table1;"
138147
139−type Columns = '[ "col1" ::: 'PGInt4, "col2" ::: 'PGInt4]
148+type Columns = '[ "col1" ::: 'NotNull 'PGInt4, "col2" ::: 'NotNull 'PGInt4]
140149type Tables = '[ "table1" ::: Columns ]
141−type SumAndCol1 = '[ "sum" ::: 'PGInt4, "col1" ::: 'PGInt4]
142−type StudentsColumns = '["name" ::: 'PGText]
150+type SumAndCol1 = '[ "sum" ::: 'NotNull 'PGInt4, "col1" ::: 'NotNull 'PGInt4]
151+type StudentsColumns = '["name" ::: 'NotNull 'PGText]
143152type StudentsTable = '["students" ::: StudentsColumns]
144153type OrderColumns =
145− [ "orderID" ::: 'PGInt4
146− , "orderVal" ::: 'PGText
147− , "customerID" ::: 'PGInt4
148− , "shipperID" ::: 'PGInt4
154+ [ "orderID" ::: 'NotNull 'PGInt4
155+ , "orderVal" ::: 'NotNull 'PGText
156+ , "customerID" ::: 'NotNull 'PGInt4
157+ , "shipperID" ::: 'NotNull 'PGInt4
149158 ]
150159type CustomerColumns =
151− [ "customerID" ::: 'PGInt4, "customerVal" ::: 'PGFloat4 ]
160+ [ "customerID" ::: 'NotNull 'PGInt4, "customerVal" ::: 'NotNull 'PGFloat4 ]
152161type ShipperColumns =
153− [ "shipperID" ::: 'PGInt4, "shipperVal" ::: 'PGBool ]
162+ [ "shipperID" ::: 'NotNull 'PGInt4, "shipperVal" ::: 'NotNull 'PGBool ]
154163type JoinTables =
155164 [ "shippers" ::: ShipperColumns
156165 , "customers" ::: CustomerColumns
157166 , "orders" ::: OrderColumns
158167 ]
159168type ValueColumns =
160− [ "orderVal" ::: 'PGText
161− , "customerVal" ::: 'PGFloat4
162− , "shipperVal" ::: 'PGBool
169+ [ "orderVal" ::: 'NotNull 'PGText
170+ , "customerVal" ::: 'NotNull 'PGFloat4
171+ , "shipperVal" ::: 'NotNull 'PGBool
163172 ]