on dev help sign in
aggregates and definitions work
5601163Eitan Chatav
squeal-postgresql/src/Squeal/PostgreSQL/Definition.hs+116−4
@@ -14,8 +14,9 @@
14#-}
14#-}
15
15
16module Squeal.PostgreSQL.Definition
16module Squeal.PostgreSQL.Definition
17− ( -- * Data Definition Language
17+ ( -- * Definition
18 Definition (UnsafeDefinition, renderDefinition)
18 Definition (UnsafeDefinition, renderDefinition)
19+ -- * Create
19 , createTable
20 , createTable
20 , TableConstraint (UnsafeTableConstraint, renderTableConstraint)
21 , TableConstraint (UnsafeTableConstraint, renderTableConstraint)
21 , check
22 , check
@@ -26,7 +27,9 @@ module Squeal.PostgreSQL.Definition
26 , renderOnDelete
27 , renderOnDelete
27 , OnUpdate (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
28 , OnUpdate (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
28 , renderOnUpdate
29 , renderOnUpdate
30+ -- * Drop
29 , dropTable
31 , dropTable
32+ -- * Alter
30 , alterTable
33 , alterTable
31 , alterTableRename
34 , alterTableRename
32 , AlterTable (UnsafeAlterTable, renderAlterTable)
35 , AlterTable (UnsafeAlterTable, renderAlterTable)
@@ -61,6 +64,10 @@ import Squeal.PostgreSQL.Schema
61statements
64statements
62-----------------------------------------}
65-----------------------------------------}
63
66
67+-- | A `Definition` is a statement that changes the schema of the
68+-- database, like a `createTable`, `dropTable`, or `alterTable` command.
69+-- `Definition`s may be composed using the `>>>` operator from the
70+-- `Category` typeclass.
64newtype Definition
71newtype Definition
65 (schema0 :: TablesType)
72 (schema0 :: TablesType)
66 (schema1 :: TablesType)
73 (schema1 :: TablesType)
@@ -76,11 +83,21 @@ instance Category Definition where
76CREATE statements
83CREATE statements
77-----------------------------------------}
84-----------------------------------------}
78
85
86+-- | `createTable` adds a table to the schema.
87+--
88+-- >>> :set -XOverloadedLabels -XDataKinds
89+-- >>> :{
90+-- renderDefinition $
91+-- createTable #tab (int `As` #a :* real `As` #b :* Nil) []
92+-- :}
93+-- "CREATE TABLE tab (a int, b real);"
79createTable
94createTable
80 :: (KnownSymbol table, SListI columns)
95 :: (KnownSymbol table, SListI columns)
81− => Alias table
96+ => Alias table -- ^ the name of the table to add
82 -> NP (Aliased TypeExpression) (column ': columns)
97 -> NP (Aliased TypeExpression) (column ': columns)
98+ -- ^ the names and datatype of each column
83 -> [TableConstraint schema (column ': columns)]
99 -> [TableConstraint schema (column ': columns)]
100+ -- ^ constraints that must hold for the table
84 -> Definition schema (Create table (column ': columns) schema)
101 -> Definition schema (Create table (column ': columns) schema)
85createTable table columns constraints = UnsafeDefinition $
102createTable table columns constraints = UnsafeDefinition $
86 "CREATE TABLE" <+> renderAlias table
103 "CREATE TABLE" <+> renderAlias table
@@ -97,18 +114,55 @@ createTable table columns constraints = UnsafeDefinition $
97 [] -> ""
114 [] -> ""
98 _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
115 _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
99
116
117+-- | Data types are a way to limit the kind of data that can be stored in a
118+-- table. For many applications, however, the constraint they provide is
119+-- too coarse. For example, a column containing a product price should
120+-- probably only accept positive values. But there is no standard data type
121+-- that accepts only positive numbers. Another issue is that you might want
122+-- to constrain column data with respect to other columns or rows.
123+-- For example, in a table containing product information,
124+-- there should be only one row for each product number.
125+-- `TableConstraint`s give you as much control over the data in your tables
126+-- as you wish. If a user attempts to store data in a column that would
127+-- violate a constraint, an error is raised. This applies
128+-- even if the value came from the default value definition.
100newtype TableConstraint
129newtype TableConstraint
101 (schema :: TablesType)
130 (schema :: TablesType)
102 (columns :: ColumnsType)
131 (columns :: ColumnsType)
103 = UnsafeTableConstraint { renderTableConstraint :: ByteString }
132 = UnsafeTableConstraint { renderTableConstraint :: ByteString }
104 deriving (GHC.Generic,Show,Eq,Ord,NFData)
133 deriving (GHC.Generic,Show,Eq,Ord,NFData)
105
134
135+-- | A `check` constraint is the most generic `TableConstraint` type.
136+-- It allows you to specify that the value in a certain column must satisfy
137+-- a Boolean (truth-value) expression.
138+--
139+-- >>> :set -XOverloadedLabels -XDataKinds
140+-- >>> :{
141+-- renderDefinition $
142+-- createTable #tab
143+-- ( (int & notNull) `As` #a :*
144+-- (int & notNull) `As` #b :* Nil )
145+-- [ check (#a .> #b) ]
146+-- :}
147+-- "CREATE TABLE tab (a int NOT NULL, b int NOT NULL, CHECK ((a > b)));"
106check
148check
107 :: Condition '[table ::: columns] 'Ungrouped '[]
149 :: Condition '[table ::: columns] 'Ungrouped '[]
108 -> TableConstraint schema columns
150 -> TableConstraint schema columns
109check condition = UnsafeTableConstraint $
151check condition = UnsafeTableConstraint $
110 "CHECK" <+> parenthesized (renderExpression condition)
152 "CHECK" <+> parenthesized (renderExpression condition)
111
153
154+-- | `unique` constraints ensure that the data contained in a column,
155+-- or a group of columns, is unique among all the rows in the table.
156+--
157+-- >>> :set -XOverloadedLabels -XDataKinds
158+-- >>> :{
159+-- renderDefinition $
160+-- createTable #tab
161+-- ( int `As` #a :*
162+-- int `As` #b :* Nil )
163+-- [ unique (Column #a :* Column #b :* Nil) ]
164+-- :}
165+-- "CREATE TABLE tab (a int, b int, UNIQUE (a, b));"
112unique
166unique
113 :: SListI subcolumns
167 :: SListI subcolumns
114 => NP (Column columns) subcolumns
168 => NP (Column columns) subcolumns
@@ -116,6 +170,19 @@ unique
116unique columns = UnsafeTableConstraint $
170unique columns = UnsafeTableConstraint $
117 "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
171 "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
118
172
173+-- | A `primaryKey` constraint indicates that a column, or group of columns,
174+-- can be used as a unique identifier for rows in the table.
175+-- This requires that the values be both unique and not null.
176+--
177+-- >>> :set -XOverloadedLabels -XDataKinds
178+-- >>> :{
179+-- renderDefinition $
180+-- createTable #tab
181+-- ( serial `As` #id :*
182+-- (text & notNull) `As` #name :* Nil )
183+-- [ primaryKey (Column #id :* Nil) ]
184+-- :}
185+-- "CREATE TABLE tab (id serial, name text NOT NULL, PRIMARY KEY (id));"
119primaryKey
186primaryKey
120 :: (SListI subcolumns, AllNotNull subcolumns)
187 :: (SListI subcolumns, AllNotNull subcolumns)
121 => NP (Column columns) subcolumns
188 => NP (Column columns) subcolumns
@@ -123,6 +190,40 @@ primaryKey
123primaryKey columns = UnsafeTableConstraint $
190primaryKey columns = UnsafeTableConstraint $
124 "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
191 "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
125
192
193+-- | A `foreignKey` specifies that the values in a column
194+-- (or a group of columns) must match the values appearing in some row of
195+-- another table. We say this maintains the referential integrity
196+-- between two related tables.
197+--
198+-- >>> :set -XOverloadedLabels -XDataKinds
199+-- >>> :{
200+-- let
201+-- definition :: Definition '[]
202+-- '[ "users" :::
203+-- '[ "id" ::: 'Optional ('NotNull 'PGint4)
204+-- , "username" ::: 'Required ('NotNull 'PGtext)
205+-- ]
206+-- , "emails" :::
207+-- '[ "id" ::: 'Optional ('NotNull 'PGint4)
208+-- , "userid" ::: 'Required ('NotNull 'PGint4)
209+-- , "email" ::: 'Required ('NotNull 'PGtext)
210+-- ]
211+-- ]
212+-- definition =
213+-- createTable #users
214+-- (serial `As` #id :* (text & notNull) `As` #username :* Nil)
215+-- [primaryKey (Column #id :* Nil)] >>>
216+-- createTable #emails
217+-- ( serial `As` #id :*
218+-- (integer & notNull) `As` #userid :*
219+-- (text & notNull) `As` #email :* Nil )
220+-- [ primaryKey (Column #id :* Nil)
221+-- , foreignKey (Column #userid :* Nil) #users (Column #id :* Nil)
222+-- OnDeleteCascade OnUpdateRestrict
223+-- ]
224+-- in renderDefinition definition
225+-- :}
226+-- "CREATE TABLE users (id serial, username text NOT NULL, PRIMARY KEY (id)); CREATE TABLE emails (id serial, userid integer NOT NULL, email text NOT NULL, PRIMARY KEY (id), FOREIGN KEY (userid) REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT);"
126foreignKey
227foreignKey
127 :: ( HasTable reftable schema refcolumns
228 :: ( HasTable reftable schema refcolumns
128 , SameTypes subcolumns refsubcolumns
229 , SameTypes subcolumns refsubcolumns
@@ -143,10 +244,15 @@ foreignKey columns reftable refcolumns onDelete onUpdate =
143 <+> renderOnDelete onDelete
244 <+> renderOnDelete onDelete
144 <+> renderOnUpdate onUpdate
245 <+> renderOnUpdate onUpdate
145
246
247+-- | `OnDelete` indicates what to do with rows that reference a deleted row.
146data OnDelete
248data OnDelete
147 = OnDeleteNoAction
249 = OnDeleteNoAction
250+ -- ^ if any referencing rows still exist when the constraint is checked,
251+ -- an error is raised
148− | OnDeleteRestrict
252+ | OnDeleteRestrict -- ^ prevents deletion of a referenced row
149 | OnDeleteCascade
253 | OnDeleteCascade
254+ -- ^ specifies that when a referenced row is deleted,
255+ -- row(s) referencing it should be automatically deleted as well
150 deriving (GHC.Generic,Show,Eq,Ord)
256 deriving (GHC.Generic,Show,Eq,Ord)
151instance NFData OnDelete
257instance NFData OnDelete
152
258
@@ -156,10 +262,16 @@ renderOnDelete = \case
156 OnDeleteRestrict -> "ON DELETE RESTRICT"
262 OnDeleteRestrict -> "ON DELETE RESTRICT"
157 OnDeleteCascade -> "ON DELETE CASCADE"
263 OnDeleteCascade -> "ON DELETE CASCADE"
158
264
265+-- | Analagous to `OnDelete` there is also `OnUpdate` which is invoked
266+-- when a referenced column is changed (updated).
159data OnUpdate
267data OnUpdate
160 = OnUpdateNoAction
268 = OnUpdateNoAction
269+ -- ^ if any referencing rows has not changed when the constraint is checked,
270+ -- an error is raised
161− | OnUpdateRestrict
271+ | OnUpdateRestrict -- ^ prevents update of a referenced row
162 | OnUpdateCascade
272 | OnUpdateCascade
273+ -- ^ the updated values of the referenced column(s) should be copied
274+ -- into the referencing row(s)
163 deriving (GHC.Generic,Show,Eq,Ord)
275 deriving (GHC.Generic,Show,Eq,Ord)
164instance NFData OnUpdate
276instance NFData OnUpdate
165
277
squeal-postgresql/src/Squeal/PostgreSQL/Expression.hs+20−22
@@ -83,25 +83,18 @@ module Squeal.PostgreSQL.Expression
83 , charLength
83 , charLength
84 , like
84 , like
85 -- ** Aggregation
85 -- ** Aggregation
86− , unsafeAggregate
87− , unsafeAggregateDistinct
86+ , unsafeAggregate, unsafeAggregateDistinct
88− , sum_
89− , sumDistinct
87+ , sum_, sumDistinct
90 , PGAvg (avg, avgDistinct)
88 , PGAvg (avg, avgDistinct)
91− , bitAnd
92− , bitOr
93− , boolAnd
89+ , bitAnd, bitOr, boolAnd, boolOr
94− , boolOr
95 , countStar
90 , countStar
96− , countDistinctStar
97− , count
91+ , count, countDistinct
98− , every
92+ , every, everyDistinct
99− , jsonAgg
93+ , jsonAgg, jsonAggDistinct
100− , jsonbAgg
94+ , jsonbAgg, jsonbAggDistinct
101 , jsonObjectAgg
95 , jsonObjectAgg
102 , jsonbObjectAgg
96 , jsonbObjectAgg
103− , max_
97+ , max_, maxDistinct, min_, minDistinct
104− , min_
105 -- * Tables
98 -- * Tables
106 , Table (UnsafeTable, renderTable)
99 , Table (UnsafeTable, renderTable)
107 , HasTable (getTable)
100 , HasTable (getTable)
@@ -707,30 +700,33 @@ boolAnd, boolOr
707boolAnd = unsafeAggregate "bool_and"
700boolAnd = unsafeAggregate "bool_and"
708boolOr = unsafeAggregate "bool_or"
701boolOr = unsafeAggregate "bool_or"
709
702
710−countStar, countDistinctStar
703+countStar
711 :: Expression tables ('Grouped bys) params ('Required ('NotNull 'PGint8))
704 :: Expression tables ('Grouped bys) params ('Required ('NotNull 'PGint8))
712countStar = UnsafeExpression $ "count(*)"
705countStar = UnsafeExpression $ "count(*)"
713−countDistinctStar = UnsafeExpression $ "count(DISTINCT *)"
714
706
715−count
707+count, countDistinct
716 :: Expression tables 'Ungrouped params ('Required ty)
708 :: Expression tables 'Ungrouped params ('Required ty)
717 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGint8))
709 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGint8))
718count = unsafeAggregate "count"
710count = unsafeAggregate "count"
711+countDistinct = unsafeAggregateDistinct "count"
719
712
720−every
713+every, everyDistinct
721 :: Expression tables 'Ungrouped params ('Required (nullity 'PGbool))
714 :: Expression tables 'Ungrouped params ('Required (nullity 'PGbool))
722 -> Expression tables ('Grouped bys) params ('Required (nullity 'PGbool))
715 -> Expression tables ('Grouped bys) params ('Required (nullity 'PGbool))
723every = unsafeAggregate "every"
716every = unsafeAggregate "every"
717+everyDistinct = unsafeAggregateDistinct "every"
724
718
725−jsonAgg
719+jsonAgg, jsonAggDistinct
726 :: Expression tables 'Ungrouped params ('Required ty)
720 :: Expression tables 'Ungrouped params ('Required ty)
727 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGjson))
721 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGjson))
728jsonAgg = unsafeAggregate "json_agg"
722jsonAgg = unsafeAggregate "json_agg"
723+jsonAggDistinct = unsafeAggregateDistinct "json_agg"
729
724
730−jsonbAgg
725+jsonbAgg, jsonbAggDistinct
731 :: Expression tables 'Ungrouped params ('Required ty)
726 :: Expression tables 'Ungrouped params ('Required ty)
732 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGjsonb))
727 -> Expression tables ('Grouped bys) params ('Required ('NotNull 'PGjsonb))
733jsonbAgg = unsafeAggregate "jsonb_agg"
728jsonbAgg = unsafeAggregate "jsonb_agg"
729+jsonbAggDistinct = unsafeAggregateDistinct "jsonb_agg"
734
730
735jsonObjectAgg
731jsonObjectAgg
736 :: Expression tables 'Ungrouped params ('Required ('NotNull keyty))
732 :: Expression tables 'Ungrouped params ('Required ('NotNull keyty))
@@ -748,11 +744,13 @@ jsonbObjectAgg k v = UnsafeExpression $
748 "jsonb_object_agg("
744 "jsonb_object_agg("
749 <> renderExpression k <> ", " <> renderExpression v <> ")"
745 <> renderExpression k <> ", " <> renderExpression v <> ")"
750
746
751−max_, min_
747+max_, min_, maxDistinct, minDistinct
752 :: Expression tables 'Ungrouped params ('Required (nullity ty))
748 :: Expression tables 'Ungrouped params ('Required (nullity ty))
753 -> Expression tables ('Grouped bys) params ('Required (nullity ty))
749 -> Expression tables ('Grouped bys) params ('Required (nullity ty))
754max_ = unsafeAggregate "max"
750max_ = unsafeAggregate "max"
755min_ = unsafeAggregate "min"
751min_ = unsafeAggregate "min"
752+maxDistinct = unsafeAggregateDistinct "max"
753+minDistinct = unsafeAggregateDistinct "min"
756
754
757{-----------------------------------------
755{-----------------------------------------
758tables
756tables