on dev help sign in
window functions - attempt 3
commit873c5111afcf27ce863e359ccf7722bc6076f13f authorEitan Chatav <eitan.chatav@gmail.com> dateThu 10 Jan 2019, 19:44 (8 years ago) parent7b9b2af
2 files changed+165−55
squeal-postgresql/src/Squeal/PostgreSQL/Expression.hs+149 squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+16−55
squeal-postgresql/src/Squeal/PostgreSQL/Expression.hs+149
@@ -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()"
squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+16−55
@@ -74,7 +74,6 @@ module Squeal.PostgreSQL.Query
7474 , where_
7575 , groupBy
7676 , having
77− , orderBy
7877 , limit
7978 , offset
8079 -- * From Clauses
@@ -92,8 +91,6 @@ module Squeal.PostgreSQL.Query
9291 , By (..)
9392 , GroupByClause (..)
9493 , HavingClause (..)
95− -- * Sorting
96− , SortExpression (..)
9794 -- * Subquery Expressions
9895 , in_
9996 , rowIn
@@ -412,6 +409,11 @@ data Selection commons schemas params grp from row where
412409 :: Selection commons schemas params grp from right
413410 -> Selection commons schemas params grp from left
414411 -> Selection commons schemas params grp from (Join left right)
412+ Over
413+ :: SListI row
414+ => NP (Aliased (WindowFunction commons schemas params grp from)) row
415+ -> WindowDefinition commons schemas params grp from
416+ -> Selection commons schemas params grp from row
415417instance (KnownSymbol col, row ~ '[col ::: ty])
416418 => Aliasable col
417419 (Expression commons schemas params grp from ty)
@@ -444,6 +446,15 @@ instance RenderSQL (Selection commons schemas params grp from row) where
444446 Star -> "*"
445447 DotStar tab -> renderSQL tab <> ".*"
446448 Also right left -> renderSQL left <> ", " <> renderSQL right
449+ Over winFns winDef ->
450+ let
451+ renderOver
452+ :: Aliased (WindowFunction commons schemas params grp from) field
453+ -> ByteString
454+ renderOver winFn = renderAliased renderSQL winFn
455+ <+> "OVER" <+> parenthesized (renderSQL winDef)
456+ in
457+ renderCommaSeparated renderOver winFns
447458
448459-- | the `TableExpression` in the `select` command constructs an intermediate
449460-- virtual table by possibly combining tables, views, eliminating rows,
@@ -662,13 +673,8 @@ having
662673having hv rels = rels
663674 { havingClause = case havingClause rels of Having hvs -> Having (hv:hvs) }
664675
665−-- | An `orderBy` is an endomorphism of `TableExpression`s which appends an
666−-- ordering to the right of the `orderByClause`.
667−orderBy
668− :: [SortExpression commons schemas params grp from] -- ^ sort expressions
669− -> TableExpression commons schemas params grp from
670− -> TableExpression commons schemas params grp from
671−orderBy srts rels = rels {orderByClause = orderByClause rels ++ srts}
676+instance OrderBy TableExpression where
677+ orderBy srts rels = rels {orderByClause = orderByClause rels ++ srts}
672678
673679-- | A `limit` is an endomorphism of `TableExpression`s which adds to the
674680-- `limitClause`.
@@ -1035,51 +1041,6 @@ instance RenderSQL (HavingClause commons schemas params grp from) where
10351041 Having conditions ->
10361042 " HAVING" <+> commaSeparated (renderSQL <$> conditions)
10371043
1038−{-----------------------------------------
1039−Sorting
1040−-----------------------------------------}
1041−
1042−-- | `SortExpression`s are used by `sortBy` to optionally sort the results
1043−-- of a `Query`. `Asc` or `Desc` set the sort direction of a `NotNull` result
1044−-- column to ascending or descending. Ascending order puts smaller values
1045−-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
1046−-- descending order is determined with the `.>` operator. `AscNullsFirst`,
1047−-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
1048−-- determine whether nulls appear before or after non-null values in the sort
1049−-- ordering of a `Null` result column.
1050−data SortExpression commons schemas params grp from where
1051− Asc
1052− :: Expression commons schemas params grp from ('NotNull ty)
1053− -> SortExpression commons schemas params grp from
1054− Desc
1055− :: Expression commons schemas params grp from ('NotNull ty)
1056− -> SortExpression commons schemas params grp from
1057− AscNullsFirst
1058− :: Expression commons schemas params grp from ('Null ty)
1059− -> SortExpression commons schemas params grp from
1060− AscNullsLast
1061− :: Expression commons schemas params grp from ('Null ty)
1062− -> SortExpression commons schemas params grp from
1063− DescNullsFirst
1064− :: Expression commons schemas params grp from ('Null ty)
1065− -> SortExpression commons schemas params grp from
1066− DescNullsLast
1067− :: Expression commons schemas params grp from ('Null ty)
1068− -> SortExpression commons schemas params grp from
1069−deriving instance Show (SortExpression commons schemas params grp from)
1070−
1071−-- | Render a `SortExpression`.
1072−instance RenderSQL (SortExpression commons schemas params grp from) where
1073− renderSQL = \case
1074− Asc expression -> renderSQL expression <+> "ASC"
1075− Desc expression -> renderSQL expression <+> "DESC"
1076− AscNullsFirst expression -> renderSQL expression
1077− <+> "ASC NULLS FIRST"
1078− DescNullsFirst expression -> renderSQL expression
1079− <+> "DESC NULLS FIRST"
1080− AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
1081− DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
1082−
10831044unsafeSubqueryExpression
10841045 :: ByteString
10851046 -> Expression commons schemas params grp from ty