@@ -56,7 +56,13 @@ module Squeal.PostgreSQL.Query
5656 , offset
5757 -- * From
5858 , FromClause (..)
59− , renderFromClause
59+ , table
60+ , subquery
61+ , crossJoin
62+ , innerJoin
63+ , leftOuterJoin
64+ , rightOuterJoin
65+ , fullOuterJoin
6066 -- * Grouping
6167 , By (By, By2)
6268 , renderBy
@@ -97,7 +103,7 @@ let
97103 '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
98104 '[]
99105 '["col" ::: 'Null 'PGint4]
100− query = selectStar (from (Table (#tab `As` #t)))
106+ query = selectStar (from (table (#tab `As` #t)))
101107in renderQuery query
102108:}
103109"SELECT * FROM tab AS t"
@@ -116,7 +122,7 @@ let
116122 query =
117123 select
118124 ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
119− ( from (Table (#tab `As` #t))
125+ ( from (table (#tab `As` #t))
120126 & where_ (#col1 .> #col2)
121127 & where_ (#col2 .> 0) )
122128in renderQuery query
@@ -133,7 +139,7 @@ let
133139 '["col" ::: 'Null 'PGint4]
134140 query =
135141 selectStar
136− (from (Subquery (selectStar (from (Table (#tab `As` #t))) `As` #sub)))
142+ (from (subquery (selectStar (from (table (#tab `As` #t))) `As` #sub)))
137143in renderQuery query
138144:}
139145"SELECT * FROM (SELECT * FROM tab AS t) AS sub"
@@ -147,7 +153,7 @@ let
147153 '[]
148154 '["col" ::: 'Null 'PGint4]
149155 query = selectStar
150− (from (Table (#tab `As` #t)) & limit 100 & offset 2 & limit 50 & offset 2)
156+ (from (table (#tab `As` #t)) & limit 100 & offset 2 & limit 50 & offset 2)
151157in renderQuery query
152158:}
153159"SELECT * FROM tab AS t LIMIT 50 OFFSET 4"
@@ -161,7 +167,7 @@ let
161167 '[ 'NotNull 'PGfloat8]
162168 '["col" ::: 'NotNull 'PGfloat8]
163169 query = selectStar
164− (from (Table (#tab `As` #t)) & where_ (#col .> param @1))
170+ (from (table (#tab `As` #t)) & where_ (#col .> param @1))
165171in renderQuery query
166172:}
167173"SELECT * FROM tab AS t WHERE (col > ($1 :: float8))"
@@ -179,7 +185,7 @@ let
179185 , "col1" ::: 'NotNull 'PGint4 ]
180186 query =
181187 select (sum_ #col2 `As` #sum :* #col1 `As` #col1 :* Nil)
182− ( from (Table (#tab `As` #table1))
188+ ( from (table (#tab `As` #table1))
183189 & group (By #col1 :* Nil)
184190 & having (#col1 + sum_ #col2 .> 1) )
185191in renderQuery query
@@ -195,7 +201,7 @@ let
195201 '[]
196202 '["col" ::: 'Null 'PGint4]
197203 query = selectStar
198− (from (Table (#tab `As` #t)) & orderBy [#col & AscNullsFirst])
204+ (from (table (#tab `As` #t)) & orderBy [#col & AscNullsFirst])
199205in renderQuery query
200206:}
201207"SELECT * FROM tab AS t ORDER BY col ASC NULLS FIRST"
@@ -235,10 +241,10 @@ let
235241 ( #o ! #price `As` #order_price :*
236242 #c ! #name `As` #customer_name :*
237243 #s ! #name `As` #shipper_name :* Nil )
238− ( from (Table (#orders `As` #o)
239− & InnerJoin (Table (#customers `As` #c))
244+ ( from (table (#orders `As` #o)
245+ & innerJoin (table (#customers `As` #c))
240246 (#o ! #customer_id .== #c ! #id)
241− & InnerJoin (Table (#shippers `As` #s))
247+ & innerJoin (table (#shippers `As` #s))
242248 (#o ! #shipper_id .== #s ! #id)) )
243249in renderQuery query
244250:}
@@ -253,7 +259,7 @@ let
253259 '[]
254260 '["col" ::: 'Null 'PGint4]
255261 query = selectDotStar #t1
256− (from (Table (#tab `As` #t1) & CrossJoin (Table (#tab `As` #t2))))
262+ (from (table (#tab `As` #t1) & crossJoin (table (#tab `As` #t2))))
257263in renderQuery query
258264:}
259265"SELECT t1.* FROM tab AS t1 CROSS JOIN tab AS t2"
@@ -267,14 +273,13 @@ let
267273 '[]
268274 '["col" ::: 'Null 'PGint4]
269275 query =
270− selectStar (from (Table (#tab `As` #t)))
276+ selectStar (from (table (#tab `As` #t)))
271277 `unionAll`
272− selectStar (from (Table (#tab `As` #t)))
278+ selectStar (from (table (#tab `As` #t)))
273279in renderQuery query
274280:}
275281"(SELECT * FROM tab AS t) UNION ALL (SELECT * FROM tab AS t)"
276282-}
277−
278283newtype Query
279284 (schema :: TablesType)
280285 (params :: [NullityType])
@@ -411,8 +416,8 @@ selectDotStar
411416 -> TableExpression schema params tables 'Ungrouped
412417 -- ^ intermediate virtual table
413418 -> Query schema params columns
414−selectDotStar table tables = UnsafeQuery $
415− "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
419+selectDotStar tab tables = UnsafeQuery $
420+ "SELECT" <+> renderAlias tab <> ".*" <+> renderTableExpression tables
416421
417422-- | A `selectDistinctDotStar` asks for all the columns of a particular table,
418423-- and eliminates duplicate rows.
@@ -423,8 +428,8 @@ selectDistinctDotStar
423428 -> TableExpression schema params tables 'Ungrouped
424429 -- ^ intermediate virtual table
425430 -> Query schema params columns
426−selectDistinctDotStar table tables = UnsafeQuery $
427− "SELECT DISTINCT" <+> renderAlias table <> ".*"
431+selectDistinctDotStar tab tables = UnsafeQuery $
432+ "SELECT DISTINCT" <+> renderAlias tab <> ".*"
428433 <+> renderTableExpression tables
429434
430435{-----------------------------------------
@@ -590,93 +595,108 @@ FROM clauses
590595{- |
591596A `FromClause` can be a table name, or a derived table such
592597as a subquery, a @JOIN@ construct, or complex combinations of these.
593−
594−* A real `Table` is a table from the schema.
595−
596−* `Subquery` derives a table from a `Query`.
597−
598−* A joined table is a table derived from two other (real or derived) tables
599−according to the rules of the particular join type. `CrossJoin`, `InnerJoin`,
600−`LeftOuterJoin`, `RightOuterJoin` and `FullOuterJoin` are available and can
601−be nested using the `&` operator to match the left-to-right sequencing of
602−their placement in SQL.
603−
604− * @t1 & CrossJoin t2@. For every possible combination of rows from
598+-}
599+newtype FromClause schema params tables
600+ = UnsafeFromClause { renderFromClause :: ByteString }
601+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
602+
603+-- | A real `table` is a table from the schema.
604+table
605+ :: Aliased (Table schema) table
606+ -> FromClause schema params '[table]
607+table = UnsafeFromClause . renderAliasedAs renderTable
608+
609+-- | `subquery` derives a table from a `Query`.
610+subquery
611+ :: Aliased (Query schema params) table
612+ -> FromClause schema params '[table]
613+subquery = UnsafeFromClause . renderAliasedAs (parenthesized . renderQuery)
614+
615+{- | @t1 & crossJoin t2@. For every possible combination of rows from
605616 @t1@ and @t2@ (i.e., a Cartesian product), the joined table will contain
606617 a row consisting of all columns in @t1@ followed by all columns in @t2@.
607618 If the tables have @n@ and @m@ rows respectively, the joined table will
608619 have @n * m@ rows.
609−
610− * @t1 & InnerJoin t2 on@. For each row @r1@ of @t1@, the joined
620+-}
621+crossJoin
622+ :: FromClause schema params right
623+ -- ^ right
624+ -> FromClause schema params left
625+ -- ^ left
626+ -> FromClause schema params (Join left right)
627+crossJoin right left = UnsafeFromClause $
628+ renderFromClause left <+> "CROSS JOIN" <+> renderFromClause right
629+
630+{- | @t1 & InnerJoin t2 on@. For each row @r1@ of @t1@, the joined
611631 table has a row for each row in @t2@ that satisfies the @on@ condition
612632 with @r1@
613−
614− * @t1 & LeftOuterJoin t2 on@. First, an inner join is performed.
633+-}
634+innerJoin
635+ :: FromClause schema params right
636+ -- ^ right
637+ -> Condition (Join left right) 'Ungrouped params
638+ -- ^ @ON@ condition
639+ -> FromClause schema params left
640+ -- ^ left
641+ -> FromClause schema params (Join left right)
642+innerJoin right on left = UnsafeFromClause $
643+ renderFromClause left <+> "INNER JOIN" <+> renderFromClause right
644+ <+> "ON" <+> renderExpression on
645+
646+{- | @t1 & LeftOuterJoin t2 on@. First, an inner join is performed.
615647 Then, for each row in @t1@ that does not satisfy the @on@ condition with
616648 any row in @t2@, a joined row is added with null values in columns of @t2@.
617649 Thus, the joined table always has at least one row for each row in @t1@.
618−
619− * @t1 & RightOuterJoin t2 on@. First, an inner join is performed.
650+-}
651+leftOuterJoin
652+ :: FromClause schema params right
653+ -- ^ right
654+ -> Condition (Join left right) 'Ungrouped params
655+ -- ^ @ON@ condition
656+ -> FromClause schema params left
657+ -- ^ left
658+ -> FromClause schema params (Join left (NullifyTables right))
659+leftOuterJoin right on left = UnsafeFromClause $
660+ renderFromClause left <+> "LEFT OUTER JOIN" <+> renderFromClause right
661+ <+> "ON" <+> renderExpression on
662+
663+{- | @t1 & RightOuterJoin t2 on@. First, an inner join is performed.
620664 Then, for each row in @t2@ that does not satisfy the @on@ condition with
621665 any row in @t1@, a joined row is added with null values in columns of @t1@.
622666 This is the converse of a left join: the result table will always
623667 have a row for each row in @t2@.
624−
625− * @t1 & FullOuterJoin t2 on@. First, an inner join is performed.
668+-}
669+rightOuterJoin
670+ :: FromClause schema params right
671+ -- ^ right
672+ -> Condition (Join left right) 'Ungrouped params
673+ -- ^ @ON@ condition
674+ -> FromClause schema params left
675+ -- ^ left
676+ -> FromClause schema params (Join (NullifyTables left) right)
677+rightOuterJoin right on left = UnsafeFromClause $
678+ renderFromClause left <+> "RIGHT OUTER JOIN" <+> renderFromClause right
679+ <+> "ON" <+> renderExpression on
680+
681+{- | @t1 & FullOuterJoin t2 on@. First, an inner join is performed.
626682 Then, for each row in @t1@ that does not satisfy the @on@ condition with
627683 any row in @t2@, a joined row is added with null values in columns of @t2@.
628684 Also, for each row of @t2@ that does not satisfy the join condition
629685 with any row in @t1@, a joined row with null values in the columns of @t1@
630686 is added.
631687-}
632−data FromClause schema params tables where
633− Table
634− :: Aliased (Table schema) table
635− -> FromClause schema params '[table]
636− Subquery
637− :: Aliased (Query schema params) table
638− -> FromClause schema params '[table]
639− CrossJoin
640− :: FromClause schema params right
641− -> FromClause schema params left
642− -> FromClause schema params (Join left right)
643− InnerJoin
644− :: FromClause schema params right
645− -> Condition (Join left right) 'Ungrouped params
646− -> FromClause schema params left
647− -> FromClause schema params (Join left right)
648− LeftOuterJoin
649− :: FromClause schema params right
650− -> Condition (Join left right) 'Ungrouped params
651− -> FromClause schema params left
652− -> FromClause schema params (Join left (NullifyTables right))
653− RightOuterJoin
654− :: FromClause schema params right
655− -> Condition (Join left right) 'Ungrouped params
656− -> FromClause schema params left
657− -> FromClause schema params (Join (NullifyTables left) right)
658− FullOuterJoin
659− :: FromClause schema params right
660− -> Condition (Join left right) 'Ungrouped params
661− -> FromClause schema params left
662− -> FromClause schema params
663− (Join (NullifyTables left) (NullifyTables right))
664−
665−-- | Renders a `FromClause`.
666−renderFromClause :: FromClause schema params tables -> ByteString
667−renderFromClause = \case
668− Table table -> renderAliasedAs renderTable table
669− Subquery selection -> renderAliasedAs (parenthesized . renderQuery) selection
670− CrossJoin right left ->
671− renderFromClause left <+> "CROSS JOIN" <+> renderFromClause right
672− InnerJoin right on left -> renderJoin "INNER JOIN" right on left
673− LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
674− RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
675− FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
676− where
677− renderJoin op right on left =
678− renderFromClause left <+> op <+> renderFromClause right
679− <+> "ON" <+> renderExpression on
688+fullOuterJoin
689+ :: FromClause schema params right
690+ -- ^ right
691+ -> Condition (Join left right) 'Ungrouped params
692+ -- ^ @ON@ condition
693+ -> FromClause schema params left
694+ -- ^ left
695+ -> FromClause schema params
696+ (Join (NullifyTables left) (NullifyTables right))
697+fullOuterJoin right on left = UnsafeFromClause $
698+ renderFromClause left <+> "FULL OUTER JOIN" <+> renderFromClause right
699+ <+> "ON" <+> renderExpression on
680700
681701{-----------------------------------------
682702Grouping
@@ -706,7 +726,7 @@ deriving instance Ord (By tables by)
706726renderBy :: By tables tabcolty -> ByteString
707727renderBy = \case
708728 By column -> renderAlias column
709− By2 (table, column) -> renderAlias table <> "." <> renderAlias column
729+ By2 (tab, column) -> renderAlias tab <> "." <> renderAlias column
710730
711731-- | A `GroupByClause` indicates the `Grouping` of a `TableExpression`.
712732-- A `NoGroups` indicates `Ungrouped` while a `Group` indicates `Grouped`.