orville-postgresql-1.0.0.0: src/Orville/PostgreSQL/Expr/OrderBy.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{- |
Copyright : Flipstone Technology Partners 2023
License : MIT
Stability : Stable
@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.OrderBy
( OrderByClause
, orderByClause
, OrderByExpr
, appendOrderByExpr
, orderByColumnName
, orderByColumnsExpr
, OrderByDirection
, NullsOrder (NullsFirst, NullsLast)
, ascendingOrder
, descendingOrder
, ascendingOrderWith
, descendingOrderWith
)
where
import qualified Data.List.NonEmpty as NEL
import Orville.PostgreSQL.Expr.Name (ColumnName)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
{- |
Type to represent a SQL order by clause. E.G.
> ORDER BY foo, bar
'OrderByClause' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.
@since 1.0.0.0
-}
newtype OrderByClause
= OrderByClause RawSql.RawSql
deriving (RawSql.SqlExpression)
orderByClause :: OrderByExpr -> OrderByClause
orderByClause expr = OrderByClause (RawSql.fromString "ORDER BY " <> RawSql.toRawSql expr)
{- |
Type to represent a SQL order by expression (the part that follows the @ORDER
BY@ in SQL). E.G.
> foo, bar
'OrderByExpr' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.
@since 1.0.0.0
-}
newtype OrderByExpr = OrderByExpr RawSql.RawSql
deriving
( -- | @since 1.0.0.0
RawSql.SqlExpression
)
{- |
@since 1.0.0.0
-}
instance Semigroup OrderByExpr where
(<>) = appendOrderByExpr
{- | Combines two 'OrderByExpr's with a comma between them.
@since 1.0.0.0
-}
appendOrderByExpr :: OrderByExpr -> OrderByExpr -> OrderByExpr
appendOrderByExpr (OrderByExpr a) (OrderByExpr b) =
OrderByExpr (a <> RawSql.commaSpace <> b)
{- | Create an 'OrderByExpr' for 'ColumnName' and 'OrderByDirection' pairs, ensuring commas as
needed.
@since 1.0.0.0
-}
orderByColumnsExpr :: NEL.NonEmpty (ColumnName, OrderByDirection) -> OrderByExpr
orderByColumnsExpr =
OrderByExpr . RawSql.intercalate RawSql.commaSpace . fmap columnOrdering
where
columnOrdering :: (ColumnName, OrderByDirection) -> RawSql.RawSql
columnOrdering (columnName, orderByDirection) =
RawSql.toRawSql columnName <> RawSql.space <> RawSql.toRawSql orderByDirection
{-- |
Orders a query by the given column name in the given order direction.
@since 1.0.0.0
-}
orderByColumnName :: ColumnName -> OrderByDirection -> OrderByExpr
orderByColumnName =
curry (orderByColumnsExpr . pure)
{- |
Type to represent a SQL order by direction expression. E.G.
> ASC
'OrderByDirection' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.
@since 1.0.0.0
-}
newtype OrderByDirection = OrderByDirection RawSql.RawSql
deriving (RawSql.SqlExpression)
{- |
Type to represent the ordering of Null, intended to be used with 'OrderByDirection'.
@since 1.0.0.0
-}
data NullsOrder
= NullsFirst
| NullsLast
deriving
( -- | @since 1.0.0.0
Eq
, -- | @since 1.0.0.0
Show
, -- | @since 1.0.0.0
Ord
, -- | @since 1.0.0.0
Enum
, -- | @since 1.0.0.0
Bounded
)
{- | The SQL ASC order direction.
@since 1.0.0.0
-}
ascendingOrder :: OrderByDirection
ascendingOrder = OrderByDirection $ RawSql.fromString "ASC"
{- | The SQL DESC order direction.
@since 1.0.0.0
-}
descendingOrder :: OrderByDirection
descendingOrder = OrderByDirection $ RawSql.fromString "DESC"
{- | The SQL ASC order direction with NULLs ordered as given.
@since 1.0.0.0
-}
ascendingOrderWith :: NullsOrder -> OrderByDirection
ascendingOrderWith no =
OrderByDirection $ RawSql.toRawSql ascendingOrder <> RawSql.space <> nullsOrder no
{- | The SQL DESC order direction with NULLs ordered as given.
@since 1.0.0.0
-}
descendingOrderWith :: NullsOrder -> OrderByDirection
descendingOrderWith no =
OrderByDirection $ RawSql.toRawSql descendingOrder <> RawSql.space <> nullsOrder no
nullsOrder :: NullsOrder -> RawSql.RawSql
nullsOrder no = case no of
NullsFirst -> RawSql.fromString "NULLS FIRST"
NullsLast -> RawSql.fromString "NULLS LAST"