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

from clauses
-}

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

module Squeal.PostgreSQL.Query.From
  ( -- * From Clause
    FromClause (..)
  , table
  , subquery
  , view
  , common
  ) where

import Control.DeepSeq
import Data.ByteString (ByteString)

import qualified GHC.Generics as GHC

import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Query
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type.List
import Squeal.PostgreSQL.Type.Schema

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

{-----------------------------------------
FROM clauses
-----------------------------------------}

{- |
A `FromClause` can be a table name, or a derived table such
as a subquery, a @JOIN@ construct, or complex combinations of these.
-}
newtype FromClause
  (lat :: FromType)
  (with :: FromType)
  (db :: SchemasType)
  (params :: [NullType])
  (from :: FromType)
  = UnsafeFromClause { FromClause lat with db params from -> ByteString
renderFromClause :: ByteString }
  deriving stock ((forall x.
 FromClause lat with db params from
 -> Rep (FromClause lat with db params from) x)
-> (forall x.
    Rep (FromClause lat with db params from) x
    -> FromClause lat with db params from)
-> Generic (FromClause lat with db params from)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType) x.
Rep (FromClause lat with db params from) x
-> FromClause lat with db params from
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType) x.
FromClause lat with db params from
-> Rep (FromClause lat with db params from) x
forall x.
Rep (FromClause lat with db params from) x
-> FromClause lat with db params from
forall x.
FromClause lat with db params from
-> Rep (FromClause lat with db params from) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType) x.
Rep (FromClause lat with db params from) x
-> FromClause lat with db params from
$cfrom :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType) x.
FromClause lat with db params from
-> Rep (FromClause lat with db params from) x
GHC.Generic,Int -> FromClause lat with db params from -> ShowS
[FromClause lat with db params from] -> ShowS
FromClause lat with db params from -> String
(Int -> FromClause lat with db params from -> ShowS)
-> (FromClause lat with db params from -> String)
-> ([FromClause lat with db params from] -> ShowS)
-> Show (FromClause lat with db params from)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
Int -> FromClause lat with db params from -> ShowS
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
[FromClause lat with db params from] -> ShowS
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FromClause lat with db params from] -> ShowS
$cshowList :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
[FromClause lat with db params from] -> ShowS
show :: FromClause lat with db params from -> String
$cshow :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from -> String
showsPrec :: Int -> FromClause lat with db params from -> ShowS
$cshowsPrec :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
Int -> FromClause lat with db params from -> ShowS
Show,FromClause lat with db params from
-> FromClause lat with db params from -> Bool
(FromClause lat with db params from
 -> FromClause lat with db params from -> Bool)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Bool)
-> Eq (FromClause lat with db params from)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c/= :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
== :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c== :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
Eq,Eq (FromClause lat with db params from)
Eq (FromClause lat with db params from)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Ordering)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Bool)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Bool)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Bool)
-> (FromClause lat with db params from
    -> FromClause lat with db params from -> Bool)
-> (FromClause lat with db params from
    -> FromClause lat with db params from
    -> FromClause lat with db params from)
-> (FromClause lat with db params from
    -> FromClause lat with db params from
    -> FromClause lat with db params from)
-> Ord (FromClause lat with db params from)
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
FromClause lat with db params from
-> FromClause lat with db params from -> Ordering
FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
Eq (FromClause lat with db params from)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Ordering
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
$cmin :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
max :: FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
$cmax :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from
-> FromClause lat with db params from
>= :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c>= :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
> :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c> :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
<= :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c<= :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
< :: FromClause lat with db params from
-> FromClause lat with db params from -> Bool
$c< :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Bool
compare :: FromClause lat with db params from
-> FromClause lat with db params from -> Ordering
$ccompare :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from
-> FromClause lat with db params from -> Ordering
$cp1Ord :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
Eq (FromClause lat with db params from)
Ord)
  deriving newtype (FromClause lat with db params from -> ()
(FromClause lat with db params from -> ())
-> NFData (FromClause lat with db params from)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from -> ()
forall a. (a -> ()) -> NFData a
rnf :: FromClause lat with db params from -> ()
$crnf :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from -> ()
NFData)
instance RenderSQL (FromClause lat with db params from) where
  renderSQL :: FromClause lat with db params from -> ByteString
renderSQL = FromClause lat with db params from -> ByteString
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
FromClause lat with db params from -> ByteString
renderFromClause

-- | A real `table` is a table from the database.
table
  :: (Has sch db schema, Has tab schema ('Table table))
  => Aliased (QualifiedAlias sch) (alias ::: tab) -- ^ (renamable) table alias
  -> FromClause lat with db params '[alias ::: TableToRow table]
table :: Aliased (QualifiedAlias sch) (alias ::: tab)
-> FromClause lat with db params '[alias ::: TableToRow table]
table (QualifiedAlias sch ty
tab `As` Alias alias
alias) = ByteString
-> FromClause
     lat with db params '[alias ::: ColumnsToRow (TableToColumns table)]
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause (ByteString
 -> FromClause
      lat
      with
      db
      params
      '[alias ::: ColumnsToRow (TableToColumns table)])
-> ByteString
-> FromClause
     lat with db params '[alias ::: ColumnsToRow (TableToColumns table)]
forall a b. (a -> b) -> a -> b
$
  QualifiedAlias sch ty -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch ty
tab ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> Alias alias -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias alias
alias

{- | `subquery` derives a table from a `Query`.
The subquery may not reference columns provided by preceding `FromClause` items.
Use `Squeal.PostgreSQL.Query.From.Join.JoinLateral`
if the subquery must reference columns provided by preceding `FromClause` items.
-}
subquery
  :: Aliased (Query lat with db params) query
  -- ^ aliased `Query`
  -> FromClause lat with db params '[query]
subquery :: Aliased (Query lat with db params) query
-> FromClause lat with db params '[query]
subquery = ByteString -> FromClause lat with db params '[query]
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause (ByteString -> FromClause lat with db params '[query])
-> (Aliased (Query lat with db params) query -> ByteString)
-> Aliased (Query lat with db params) query
-> FromClause lat with db params '[query]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (ty :: RowType). Query lat with db params ty -> ByteString)
-> Aliased (Query lat with db params) query -> ByteString
forall k (expression :: k -> *) (aliased :: (Symbol, k)).
(forall (ty :: k). expression ty -> ByteString)
-> Aliased expression aliased -> ByteString
renderAliased (ByteString -> ByteString
parenthesized (ByteString -> ByteString)
-> (Query lat with db params ty -> ByteString)
-> Query lat with db params ty
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query lat with db params ty -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL)

-- | `view` derives a table from a `View`.
view
  :: (Has sch db schema, Has vw schema ('View view))
  => Aliased (QualifiedAlias sch) (alias ::: vw) -- ^ (renamable) view alias
  -> FromClause lat with db params '[alias ::: view]
view :: Aliased (QualifiedAlias sch) (alias ::: vw)
-> FromClause lat with db params '[alias ::: view]
view (QualifiedAlias sch ty
vw `As` Alias alias
alias) = ByteString -> FromClause lat with db params '[alias ::: view]
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause (ByteString -> FromClause lat with db params '[alias ::: view])
-> ByteString -> FromClause lat with db params '[alias ::: view]
forall a b. (a -> b) -> a -> b
$
  QualifiedAlias sch ty -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch ty
vw ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> Alias alias -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias alias
alias

-- | `common` derives a table from a common table expression.
common
  :: Has cte with common
  => Aliased Alias (alias ::: cte) -- ^ (renamable) common table expression alias
  -> FromClause lat with db params '[alias ::: common]
common :: Aliased Alias (alias ::: cte)
-> FromClause lat with db params '[alias ::: common]
common (Alias ty
cte `As` Alias alias
alias) = ByteString -> FromClause lat with db params '[alias ::: common]
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause (ByteString -> FromClause lat with db params '[alias ::: common])
-> ByteString -> FromClause lat with db params '[alias ::: common]
forall a b. (a -> b) -> a -> b
$
  Alias ty -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias ty
cte ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> Alias alias -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias alias
alias

instance Additional (FromClause lat with db params) where
  also :: FromClause lat with db params ys
-> FromClause lat with db params xs
-> FromClause lat with db params (Join xs ys)
also FromClause lat with db params ys
right FromClause lat with db params xs
left = ByteString -> FromClause lat with db params (Join xs ys)
forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (from :: FromType).
ByteString -> FromClause lat with db params from
UnsafeFromClause (ByteString -> FromClause lat with db params (Join xs ys))
-> ByteString -> FromClause lat with db params (Join xs ys)
forall a b. (a -> b) -> a -> b
$
    FromClause lat with db params xs -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params xs
left ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
", " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> FromClause lat with db params ys -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL FromClause lat with db params ys
right