{-|
Module: Squeal.PostgreSQL.Query.From.Join
Description: Squeal joins
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental

Squeal joins
-}

{-# LANGUAGE
    ConstraintKinds
  , DeriveGeneric
  , DerivingStrategies
  , FlexibleContexts
  , FlexibleInstances
  , GADTs
  , GeneralizedNewtypeDeriving
  , LambdaCase
  , MultiParamTypeClasses
  , OverloadedLabels
  , OverloadedStrings
  , QuantifiedConstraints
  , ScopedTypeVariables
  , StandaloneDeriving
  , TypeApplications
  , TypeFamilies
  , DataKinds
  , PolyKinds
  , TypeOperators
  , RankNTypes
  , UndecidableInstances
  #-}

module Squeal.PostgreSQL.Query.From.Join
  ( -- * Join
    JoinItem (..)
  , cross, crossJoin, crossJoinLateral
  , inner, innerJoin, innerJoinLateral
  , leftOuter, leftOuterJoin, leftOuterJoinLateral
  , rightOuter, rightOuterJoin, rightOuterJoinLateral
  , fullOuter, fullOuterJoin, fullOuterJoinLateral
  ) where

import Generics.SOP hiding (from)

import qualified Generics.SOP as SOP

import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Expression
import Squeal.PostgreSQL.Expression.Logic
import Squeal.PostgreSQL.Query
import Squeal.PostgreSQL.Query.From
import Squeal.PostgreSQL.Query.From.Set
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type.List
import Squeal.PostgreSQL.Type.Schema

-- $setup
-- >>> import Squeal.PostgreSQL

{- |
A `JoinItem` is the right hand side of a `cross`,
`inner`, `leftOuter`, `rightOuter`, `fullOuter` join of
`FromClause`s.
-}
data JoinItem
  (lat :: FromType)
  (with :: FromType)
  (db :: SchemasType)
  (params :: [NullType])
  (left :: FromType)
  (right :: FromType) where
    Join
      :: FromClause lat with db params right
      -- ^ A standard `Squeal.PostgreSQL.Query.Join`.
      -- It is not allowed to reference columns provided
      -- by preceding `FromClause` items.
      -> JoinItem lat with db params left right
    JoinLateral
      :: Aliased (Query (Join lat left) with db params) query
      -- ^ Subqueries can be preceded by `JoinLateral`.
      -- This allows them to reference columns provided
      -- by preceding `FromClause` items.
      -> JoinItem lat with db params left '[query]
    JoinFunction
      :: SetFun db arg set
      -- ^ Set returning functions can be preceded by `JoinFunction`.
      -- This allows them to reference columns provided
      -- by preceding `FromClause` items.
      -> Expression 'Ungrouped lat with db params left arg
      -- ^ argument
      -> JoinItem lat with db params left '[set]
    JoinFunctionN
      :: SListI args
      => SetFunN db args set
      -- ^ Set returning multi-argument functions
      -- can be preceded by `JoinFunctionN`.
      -- This allows them to reference columns provided
      -- by preceding `FromClause` items.
      -> NP (Expression 'Ungrouped lat with db params left) args
      -- ^ arguments
      -> JoinItem lat with db params left '[set]
instance RenderSQL (JoinItem lat with db params left right) where
  renderSQL :: JoinItem lat with db params left right -> ByteString
renderSQL = \case
    Join FromClause lat with db params right
tab -> ByteString
"JOIN" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params right
tab
    JoinLateral Aliased (Query (Join lat left) with db params) query
qry -> ByteString
"JOIN LATERAL" ByteString -> ByteString -> ByteString
<+>
      forall {k} (expression :: k -> *) (aliased :: (Symbol, k)).
(forall (ty :: k). expression ty -> ByteString)
-> Aliased expression aliased -> ByteString
renderAliased (ByteString -> ByteString
parenthesized forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall sql. RenderSQL sql => sql -> ByteString
renderSQL) Aliased (Query (Join lat left) with db params) query
qry
    JoinFunction SetFun db arg set
fun Expression 'Ungrouped lat with db params left arg
x -> ByteString
"JOIN" ByteString -> ByteString -> ByteString
<+>
      forall sql. RenderSQL sql => sql -> ByteString
renderSQL (SetFun db arg set
fun (forall (grp :: Grouping) (lat :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType]) (from :: FromType)
       (ty :: NullType).
ByteString -> Expression grp lat with db params from ty
UnsafeExpression (forall sql. RenderSQL sql => sql -> ByteString
renderSQL Expression 'Ungrouped lat with db params left arg
x)))
    JoinFunctionN SetFunN db args set
fun NP (Expression 'Ungrouped lat with db params left) args
xs -> ByteString
"JOIN" ByteString -> ByteString -> ByteString
<+>
      forall sql. RenderSQL sql => sql -> ByteString
renderSQL (SetFunN db args set
fun (forall {k} {l} (h :: (k -> *) -> l -> *) (xs :: l) (f :: k -> *)
       (f' :: k -> *).
(SListIN (Prod h) xs, HAp h) =>
(forall (a :: k). f a -> f' a) -> h f xs -> h f' xs
SOP.hmap (forall (grp :: Grouping) (lat :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType]) (from :: FromType)
       (ty :: NullType).
ByteString -> Expression grp lat with db params from ty
UnsafeExpression forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall sql. RenderSQL sql => sql -> ByteString
renderSQL) NP (Expression 'Ungrouped lat with db params left) args
xs))

{- |
@left & cross (Join right)@. For every possible combination of rows from
@left@ and @right@ (i.e., a Cartesian product), the joined table will contain
a row consisting of all columns in @left@ followed by all columns in @right@.
If the tables have @n@ and @m@ rows respectively, the joined table will
have @n * m@ rows.
-}
cross
  :: JoinItem lat with db params left right -- ^ right
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left right)
cross :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
cross JoinItem lat with db params left right
item FromClause lat with db params left
tab = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause forall a b. (a -> b) -> a -> b
$
  forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params left
tab ByteString -> ByteString -> ByteString
<+> ByteString
"CROSS" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL JoinItem lat with db params left right
item

{- |
@left & crossJoin right@. For every possible combination of rows from
@left@ and @right@ (i.e., a Cartesian product), the joined table will contain
a row consisting of all columns in @left@ followed by all columns in @right@.
If the tables have @n@ and @m@ rows respectively, the joined table will
have @n * m@ rows.
-}
crossJoin
  :: FromClause lat with db params right -- ^ right
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left right)
crossJoin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
crossJoin = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
cross forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> JoinItem lat with db params left right
Join

{- |
Like `crossJoin` with a `subquery` but allowed to reference columns provided
by preceding `FromClause` items.
-}
crossJoinLateral
  :: Aliased (Query (Join lat left) with db params) query -- ^ right subquery
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left '[query])
crossJoinLateral :: forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> FromClause lat with db params left
-> FromClause lat with db params (Join left '[query])
crossJoinLateral = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
cross forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> JoinItem lat with db params left '[query]
JoinLateral

{- | @left & inner (Join right) on@. The joined table is filtered by
the @on@ condition.
-}
inner
  :: JoinItem lat with db params left right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left right)
inner :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
inner JoinItem lat with db params left right
item Condition 'Ungrouped lat with db params (Join left right)
on FromClause lat with db params left
tab = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause forall a b. (a -> b) -> a -> b
$
  forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params left
tab ByteString -> ByteString -> ByteString
<+> ByteString
"INNER" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL JoinItem lat with db params left right
item ByteString -> ByteString -> ByteString
<+> ByteString
"ON" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Condition 'Ungrouped lat with db params (Join left right)
on

{- | @left & innerJoin right on@. The joined table is filtered by
the @on@ condition.
-}
innerJoin
  :: FromClause lat with db params right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left right)
innerJoin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
innerJoin = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
inner forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> JoinItem lat with db params left right
Join

{- |
Like `innerJoin` with a `subquery` but allowed to reference columns provided
by preceding `FromClause` items.
-}
innerJoinLateral
  :: Aliased (Query (Join lat left) with db params) query -- ^ right subquery
  -> Condition 'Ungrouped lat with db params (Join left '[query]) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left '[query])
innerJoinLateral :: forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> Condition 'Ungrouped lat with db params (Join left '[query])
-> FromClause lat with db params left
-> FromClause lat with db params (Join left '[query])
innerJoinLateral = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left right)
inner forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> JoinItem lat with db params left '[query]
JoinLateral

{- | @left & leftOuter (Join right) on@. First, an inner join is performed.
Then, for each row in @left@ that does not satisfy the @on@ condition with
any row in @right@, a joined row is added with null values in columns of @right@.
Thus, the joined table always has at least one row for each row in @left@.
-}
leftOuter
  :: JoinItem lat with db params left right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left (NullifyFrom right))
leftOuter :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left (NullifyFrom right))
leftOuter JoinItem lat with db params left right
item Condition 'Ungrouped lat with db params (Join left right)
on FromClause lat with db params left
tab = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause forall a b. (a -> b) -> a -> b
$
  forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params left
tab ByteString -> ByteString -> ByteString
<+> ByteString
"LEFT OUTER" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL JoinItem lat with db params left right
item ByteString -> ByteString -> ByteString
<+> ByteString
"ON" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Condition 'Ungrouped lat with db params (Join left right)
on

{- | @left & leftOuterJoin right on@. First, an inner join is performed.
Then, for each row in @left@ that does not satisfy the @on@ condition with
any row in @right@, a joined row is added with null values in columns of @right@.
Thus, the joined table always has at least one row for each row in @left@.
-}
leftOuterJoin
  :: FromClause lat with db params right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left (NullifyFrom right))
leftOuterJoin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left (NullifyFrom right))
leftOuterJoin = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left (NullifyFrom right))
leftOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> JoinItem lat with db params left right
Join

{- |
Like `leftOuterJoin` with a `subquery` but allowed to reference columns provided
by preceding `FromClause` items.
-}
leftOuterJoinLateral
  :: Aliased (Query (Join lat left) with db params) query -- ^ right subquery
  -> Condition 'Ungrouped lat with db params (Join left '[query]) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join left (NullifyFrom '[query]))
leftOuterJoinLateral :: forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> Condition 'Ungrouped lat with db params (Join left '[query])
-> FromClause lat with db params left
-> FromClause lat with db params (Join left (NullifyFrom '[query]))
leftOuterJoinLateral = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join left (NullifyFrom right))
leftOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> JoinItem lat with db params left '[query]
JoinLateral

{- | @left & rightOuter (Join right) on@. First, an inner join is performed.
Then, for each row in @right@ that does not satisfy the @on@ condition with
any row in @left@, a joined row is added with null values in columns of @left@.
This is the converse of a left join: the result table will always
have a row for each row in @right@.
-}
rightOuter
  :: JoinItem lat with db params left right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuter :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuter JoinItem lat with db params left right
item Condition 'Ungrouped lat with db params (Join left right)
on FromClause lat with db params left
tab = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause forall a b. (a -> b) -> a -> b
$
  forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params left
tab ByteString -> ByteString -> ByteString
<+> ByteString
"RIGHT OUTER" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL JoinItem lat with db params left right
item ByteString -> ByteString -> ByteString
<+> ByteString
"ON" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Condition 'Ungrouped lat with db params (Join left right)
on

{- | @left & rightOuterJoin right on@. First, an inner join is performed.
Then, for each row in @right@ that does not satisfy the @on@ condition with
any row in @left@, a joined row is added with null values in columns of @left@.
This is the converse of a left join: the result table will always
have a row for each row in @right@.
-}
rightOuterJoin
  :: FromClause lat with db params right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuterJoin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuterJoin = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> JoinItem lat with db params left right
Join

{- |
Like `rightOuterJoin` with a `subquery` but allowed to reference columns provided
by preceding `FromClause` items.
-}
rightOuterJoinLateral
  :: Aliased (Query (Join lat left) with db params) query -- ^ right subquery
  -> Condition 'Ungrouped lat with db params (Join left '[query]) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (Join (NullifyFrom left) '[query])
rightOuterJoinLateral :: forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> Condition 'Ungrouped lat with db params (Join left '[query])
-> FromClause lat with db params left
-> FromClause lat with db params (Join (NullifyFrom left) '[query])
rightOuterJoinLateral = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (Join (NullifyFrom left) right)
rightOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> JoinItem lat with db params left '[query]
JoinLateral

{- | @left & fullOuter (Join right) on@. First, an inner join is performed.
Then, for each row in @left@ that does not satisfy the @on@ condition with
any row in @right@, a joined row is added with null values in columns of @right@.
Also, for each row of @right@ that does not satisfy the join condition
with any row in @left@, a joined row with null values in the columns of @left@
is added.
-}
fullOuter
  :: JoinItem lat with db params left right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (NullifyFrom (Join left right))
fullOuter :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (NullifyFrom (Join left right))
fullOuter JoinItem lat with db params left right
item Condition 'Ungrouped lat with db params (Join left right)
on FromClause lat with db params left
tab = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause forall a b. (a -> b) -> a -> b
$
  forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params left
tab ByteString -> ByteString -> ByteString
<+> ByteString
"FULL OUTER" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL JoinItem lat with db params left right
item ByteString -> ByteString -> ByteString
<+> ByteString
"ON" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Condition 'Ungrouped lat with db params (Join left right)
on

{- | @left & fullOuterJoin right on@. First, an inner join is performed.
Then, for each row in @left@ that does not satisfy the @on@ condition with
any row in @right@, a joined row is added with null values in columns of @right@.
Also, for each row of @right@ that does not satisfy the join condition
with any row in @left@, a joined row with null values in the columns of @left@
is added.
-}
fullOuterJoin
  :: FromClause lat with db params right -- ^ right
  -> Condition 'Ungrouped lat with db params (Join left right) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (NullifyFrom (Join left right))
fullOuterJoin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (NullifyFrom (Join left right))
fullOuterJoin = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (NullifyFrom (Join left right))
fullOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (right :: FromType) (left :: FromType).
FromClause lat with db params right
-> JoinItem lat with db params left right
Join

{- |
Like `fullOuterJoin` with a `subquery` but allowed to reference columns provided
by preceding `FromClause` items.
-}
fullOuterJoinLateral
  :: Aliased (Query (Join lat left) with db params) query -- ^ right subquery
  -> Condition 'Ungrouped lat with db params (Join left '[query]) -- ^ @ON@ condition
  -> FromClause lat with db params left -- ^ left
  -> FromClause lat with db params (NullifyFrom (Join left '[query]))
fullOuterJoinLateral :: forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> Condition 'Ungrouped lat with db params (Join left '[query])
-> FromClause lat with db params left
-> FromClause lat with db params (NullifyFrom (Join left '[query]))
fullOuterJoinLateral = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (left :: FromType) (right :: FromType).
JoinItem lat with db params left right
-> Condition 'Ungrouped lat with db params (Join left right)
-> FromClause lat with db params left
-> FromClause lat with db params (NullifyFrom (Join left right))
fullOuter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (lat :: FromType) (left :: FromType) (with :: FromType)
       (db :: SchemasType) (params :: [NullType])
       (query :: (Symbol, RowType)).
Aliased (Query (Join lat left) with db params) query
-> JoinItem lat with db params left '[query]
JoinLateral