{-# 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.Select
(
select
, select_
, selectDistinct
, selectDistinct_
, selectDistinctOn
, selectDistinctOn_
, Selection (..)
) where
import Data.ByteString (ByteString)
import Data.String
import Generics.SOP hiding (from)
import GHC.TypeLits
import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Expression
import Squeal.PostgreSQL.Expression.Sort
import Squeal.PostgreSQL.Expression.Window
import Squeal.PostgreSQL.Query
import Squeal.PostgreSQL.Query.Table
import Squeal.PostgreSQL.Type.List
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type.Schema
data Selection grp lat with db params from row where
Star
:: HasUnique tab from row
=> Selection 'Ungrouped lat with db params from row
DotStar
:: Has tab from row
=> Alias tab
-> Selection 'Ungrouped lat with db params from row
List
:: SListI row
=> NP (Aliased (Expression grp lat with db params from)) row
-> Selection grp lat with db params from row
Over
:: SListI row
=> NP (Aliased (WindowFunction grp lat with db params from)) row
-> WindowDefinition grp lat with db params from
-> Selection grp lat with db params from row
Also
:: Selection grp lat with db params from right
-> Selection grp lat with db params from left
-> Selection grp lat with db params from (Join left right)
instance Additional (Selection grp lat with db params from) where
also = Also
instance (KnownSymbol col, row ~ '[col ::: ty])
=> Aliasable col
(Expression grp lat with db params from ty)
(Selection grp lat with db params from row) where
expr `as` col = List (expr `as` col)
instance (Has tab (Join lat from) row0, Has col row0 ty, row1 ~ '[col ::: ty])
=> IsQualified tab col
(Selection 'Ungrouped lat with db params from row1) where
tab ! col = tab ! col `as` col
instance
( Has tab (Join lat from) row0
, Has col row0 ty
, row1 ~ '[col ::: ty]
, GroupedBy tab col bys )
=> IsQualified tab col
(Selection ('Grouped bys) lat with db params from row1) where
tab ! col = tab ! col `as` col
instance (HasUnique tab (Join lat from) row0, Has col row0 ty, row1 ~ '[col ::: ty])
=> IsLabel col
(Selection 'Ungrouped lat with db params from row1) where
fromLabel = fromLabel @col `as` Alias
instance
( HasUnique tab (Join lat from) row0
, Has col row0 ty
, row1 ~ '[col ::: ty]
, GroupedBy tab col bys )
=> IsLabel col
(Selection ('Grouped bys) lat with db params from row1) where
fromLabel = fromLabel @col `as` Alias
instance RenderSQL (Selection grp lat with db params from row) where
renderSQL = \case
List list -> renderCommaSeparated (renderAliased renderSQL) list
Star -> "*"
DotStar tab -> renderSQL tab <> ".*"
Also right left -> renderSQL left <> ", " <> renderSQL right
Over winFns winDef ->
let
renderOver
:: Aliased (WindowFunction grp lat with db params from) field
-> ByteString
renderOver (winFn `As` col) = renderSQL winFn
<+> "OVER" <+> parenthesized (renderSQL winDef)
<+> "AS" <+> renderSQL col
in
renderCommaSeparated renderOver winFns
instance IsString
(Selection grp lat with db params from '["fromOnly" ::: 'NotNull 'PGtext]) where
fromString str = fromString str `as` Alias
select
:: (SListI row, row ~ (x ': xs))
=> Selection grp lat with db params from row
-> TableExpression grp lat with db params from
-> Query lat with db params row
select selection tabexpr = UnsafeQuery $
"SELECT"
<+> renderSQL selection
<+> renderSQL tabexpr
select_
:: (SListI row, row ~ (x ': xs))
=> NP (Aliased (Expression grp lat with db params from)) row
-> TableExpression grp lat with db params from
-> Query lat with db params row
select_ = select . List
selectDistinct
:: (SListI columns, columns ~ (col ': cols))
=> Selection grp lat with db params from columns
-> TableExpression grp lat with db params from
-> Query lat with db params columns
selectDistinct selection tabexpr = UnsafeQuery $
"SELECT DISTINCT"
<+> renderSQL selection
<+> renderSQL tabexpr
selectDistinct_
:: (SListI columns, columns ~ (col ': cols))
=> NP (Aliased (Expression grp lat with db params from)) columns
-> TableExpression grp lat with db params from
-> Query lat with db params columns
selectDistinct_ = selectDistinct . List
selectDistinctOn
:: (SListI columns, columns ~ (col ': cols))
=> [SortExpression grp lat with db params from]
-> Selection grp lat with db params from columns
-> TableExpression grp lat with db params from
-> Query lat with db params columns
selectDistinctOn distincts selection tab = UnsafeQuery $
"SELECT DISTINCT ON"
<+> parenthesized (commaSeparated (renderDistinctOn <$> distincts))
<+> renderSQL selection
<+> renderSQL (tab {orderByClause = distincts <> orderByClause tab})
where
renderDistinctOn = \case
Asc expression -> renderSQL expression
Desc expression -> renderSQL expression
AscNullsFirst expression -> renderSQL expression
DescNullsFirst expression -> renderSQL expression
AscNullsLast expression -> renderSQL expression
DescNullsLast expression -> renderSQL expression
selectDistinctOn_
:: (SListI columns, columns ~ (col ': cols))
=> [SortExpression grp lat with db params from]
-> NP (Aliased (Expression grp lat with db params from)) columns
-> TableExpression grp lat with db params from
-> Query lat with db params columns
selectDistinctOn_ distincts = selectDistinctOn distincts . List