@@ -14,8 +14,9 @@
16module Squeal.PostgreSQL.Definition
16module Squeal.PostgreSQL.Definition
17− ( -- * Data Definition Language
17+ ( -- * Definition
18 Definition (UnsafeDefinition, renderDefinition)
18 Definition (UnsafeDefinition, renderDefinition)
19 , createTable
20 , createTable
20 , TableConstraint (UnsafeTableConstraint, renderTableConstraint)
21 , TableConstraint (UnsafeTableConstraint, renderTableConstraint)
@@ -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
29 , dropTable
31 , dropTable
30 , alterTable
33 , alterTable
31 , alterTableRename
34 , alterTableRename
32 , AlterTable (UnsafeAlterTable, renderAlterTable)
35 , AlterTable (UnsafeAlterTable, renderAlterTable)
@@ -61,6 +64,10 @@ import Squeal.PostgreSQL.Schema
62-----------------------------------------}
65-----------------------------------------}
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-----------------------------------------}
86+-- | `createTable` adds a table to the schema.
88+-- >>> :set -XOverloadedLabels -XDataKinds
91+-- createTable #tab (int `As` #a :* real `As` #b :* Nil) []
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 $
98 _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
115 _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
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)
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.
139+-- >>> :set -XOverloadedLabels -XDataKinds
141+-- renderDefinition $
143+-- ( (int & notNull) `As` #a :*
144+-- (int & notNull) `As` #b :* Nil )
145+-- [ check (#a .> #b) ]
147+-- "CREATE TABLE tab (a int NOT NULL, b int NOT NULL, CHECK ((a > b)));"
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)
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.
157+-- >>> :set -XOverloadedLabels -XDataKinds
159+-- renderDefinition $
162+-- int `As` #b :* Nil )
163+-- [ unique (Column #a :* Column #b :* Nil) ]
165+-- "CREATE TABLE tab (a int, b int, UNIQUE (a, b));"
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)
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.
177+-- >>> :set -XOverloadedLabels -XDataKinds
179+-- renderDefinition $
181+-- ( serial `As` #id :*
182+-- (text & notNull) `As` #name :* Nil )
183+-- [ primaryKey (Column #id :* Nil) ]
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)
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.
198+-- >>> :set -XOverloadedLabels -XDataKinds
201+-- definition :: Definition '[]
203+-- '[ "id" ::: 'Optional ('NotNull 'PGint4)
204+-- , "username" ::: 'Required ('NotNull 'PGtext)
207+-- '[ "id" ::: 'Optional ('NotNull 'PGint4)
208+-- , "userid" ::: 'Required ('NotNull 'PGint4)
209+-- , "email" ::: 'Required ('NotNull 'PGtext)
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
224+-- in renderDefinition definition
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
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
@@ -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"
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