@@ -16,11 +16,13 @@ Squeal expressions are the atoms used to build statements.
1616 , FlexibleContexts
1717 , FlexibleInstances
1818 , FunctionalDependencies
19+ , GADTs
1920 , GeneralizedNewtypeDeriving
2021 , LambdaCase
2122 , MagicHash
2223 , OverloadedStrings
2324 , ScopedTypeVariables
25+ , StandaloneDeriving
2426 , TypeApplications
2527 , TypeFamilies
2628 , TypeInType
@@ -141,6 +143,18 @@ module Squeal.PostgreSQL.Expression
141143 , count, countDistinct
142144 , every, everyDistinct
143145 , max_, maxDistinct, min_, minDistinct
146+ -- * Window Functions
147+ , WindowDefinition (..)
148+ , partitionBy
149+ , WindowFunction (..)
150+ , rank
151+ , rowNumber
152+ , denseRank
153+ , percentRank
154+ , cumeDist
155+ -- * Sorting
156+ , SortExpression (..)
157+ , OrderBy (..)
144158 -- * Types
145159 , TypeExpression (..)
146160 , PGTyped (..)
@@ -1778,3 +1792,138 @@ instance PGTyped schemas ty
17781792instance (KnownNat n, PGTyped schemas ty)
17791793 => PGTyped schemas (nullity ('PGfixarray n ty)) where
17801794 pgtype = fixarray @n (pgtype @schemas @ty)
1795+
1796+{-----------------------------------------
1797+Sorting
1798+-----------------------------------------}
1799+
1800+-- | `SortExpression`s are used by `sortBy` to optionally sort the results
1801+-- of a `Query`. `Asc` or `Desc` set the sort direction of a `NotNull` result
1802+-- column to ascending or descending. Ascending order puts smaller values
1803+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
1804+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
1805+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
1806+-- determine whether nulls appear before or after non-null values in the sort
1807+-- ordering of a `Null` result column.
1808+data SortExpression commons schemas params grp from where
1809+ Asc
1810+ :: Expression commons schemas params grp from ('NotNull ty)
1811+ -> SortExpression commons schemas params grp from
1812+ Desc
1813+ :: Expression commons schemas params grp from ('NotNull ty)
1814+ -> SortExpression commons schemas params grp from
1815+ AscNullsFirst
1816+ :: Expression commons schemas params grp from ('Null ty)
1817+ -> SortExpression commons schemas params grp from
1818+ AscNullsLast
1819+ :: Expression commons schemas params grp from ('Null ty)
1820+ -> SortExpression commons schemas params grp from
1821+ DescNullsFirst
1822+ :: Expression commons schemas params grp from ('Null ty)
1823+ -> SortExpression commons schemas params grp from
1824+ DescNullsLast
1825+ :: Expression commons schemas params grp from ('Null ty)
1826+ -> SortExpression commons schemas params grp from
1827+deriving instance Show (SortExpression commons schemas params grp from)
1828+
1829+class OrderBy expr where
1830+ orderBy
1831+ :: [SortExpression commons schemas params grp from]
1832+ -> expr commons schemas params grp from
1833+ -> expr commons schemas params grp from
1834+
1835+-- | Render a `SortExpression`.
1836+instance RenderSQL (SortExpression commons schemas params grp from) where
1837+ renderSQL = \case
1838+ Asc expression -> renderSQL expression <+> "ASC"
1839+ Desc expression -> renderSQL expression <+> "DESC"
1840+ AscNullsFirst expression -> renderSQL expression
1841+ <+> "ASC NULLS FIRST"
1842+ DescNullsFirst expression -> renderSQL expression
1843+ <+> "DESC NULLS FIRST"
1844+ AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
1845+ DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
1846+
1847+data WindowDefinition commons schemas params grp from where
1848+ WindowDefinition
1849+ :: SListI bys
1850+ => NP (Expression commons schemas params grp from) bys
1851+ -> [SortExpression commons schemas params grp from]
1852+ -> WindowDefinition commons schemas params grp from
1853+
1854+instance OrderBy WindowDefinition where
1855+ orderBy sortsR (WindowDefinition parts sortsL)
1856+ = WindowDefinition parts (sortsL ++ sortsR)
1857+
1858+instance RenderSQL (WindowDefinition commons schemas from grp params) where
1859+ renderSQL (WindowDefinition part ord) =
1860+ renderPartitionByClause part <> renderOrderByClause ord
1861+ where
1862+ renderPartitionByClause = \case
1863+ Nil -> ""
1864+ parts -> "PARTITION" <+> "BY" <+> renderCommaSeparated renderExpression parts
1865+ renderOrderByClause = \case
1866+ [] -> ""
1867+ srts -> " ORDER" <+> "BY"
1868+ <+> commaSeparated (renderSQL <$> srts)
1869+
1870+partitionBy
1871+ :: SListI bys
1872+ => NP (Expression commons schemas params grp from) bys
1873+ -> WindowDefinition commons schemas params grp from
1874+partitionBy bys = WindowDefinition bys []
1875+
1876+newtype WindowFunction
1877+ (commons :: FromType)
1878+ (schemas :: SchemasType)
1879+ (params :: [NullityType])
1880+ (grp :: Grouping)
1881+ (from :: FromType)
1882+ (ty :: NullityType)
1883+ = UnsafeWindowFunction { renderWindowFunction :: ByteString }
1884+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1885+
1886+instance RenderSQL (WindowFunction commons schemas params grp from ty) where
1887+ renderSQL = renderWindowFunction
1888+
1889+{- | rank of the current row with gaps; same as `rowNumber` of its first peer
1890+>>> :{
1891+let
1892+ expr :: WindowExpression commons schemas params 'Ungrouped '["tab" ::: '["a" ::: ty]] ('NotNull 'PGint8)
1893+ expr = rank `Over` (partitionBy #a)
1894+in
1895+ printSQL expr
1896+:}
1897+rank() OVER (PARTITION BY "a")
1898+-}
1899+rank :: WindowFunction commons schemas from grp params ('NotNull 'PGint8)
1900+rank = UnsafeWindowFunction "rank()"
1901+
1902+{- | number of the current row within its partition, counting from 1
1903+>>> printSQL rowNumber
1904+row_number()
1905+-}
1906+rowNumber :: WindowFunction commons schemas from grp params ('NotNull 'PGint8)
1907+rowNumber = UnsafeWindowFunction "row_number()"
1908+
1909+{- | rank of the current row without gaps; this function counts peer groups
1910+>>> printSQL denseRank
1911+dense_rank()
1912+-}
1913+denseRank :: WindowFunction commons schemas from grp params ('NotNull 'PGint8)
1914+denseRank = UnsafeWindowFunction "dense_rank()"
1915+
1916+{- | relative rank of the current row: (rank - 1) / (total partition rows - 1)
1917+>>> printSQL percentRank
1918+percent_rank()
1919+-}
1920+percentRank :: WindowFunction commons schemas from grp params ('NotNull 'PGfloat8)
1921+percentRank = UnsafeWindowFunction "percent_rank()"
1922+
1923+{- | cumulative distribution: (number of partition rows
1924+preceding or peer with current row) / total partition rows
1925+>>> printSQL cumeDist
1926+cume_dist()
1927+-}
1928+cumeDist :: WindowFunction commons schemas from grp params ('NotNull 'PGfloat8)
1929+cumeDist = UnsafeWindowFunction "cume_dist()"