@@ -34,16 +34,10 @@ module Squeal.PostgreSQL.Definition.Table
34 , createTableIfNotExists
34 , createTableIfNotExists
35 , dropTable
35 , dropTable
36 , dropTableIfExists
36 , dropTableIfExists
37− , TableConstraintExpression (..)
43− , OnDeleteClause (..)
44− , OnUpdateClause (..)
45 , alterTable
37 , alterTable
46 , alterTableRename
39 , alterTableRename
40+ , alterTableIfExistsRename
47 , AlterTable (..)
41 , AlterTable (..)
48 , addConstraint
42 , addConstraint
49 , dropConstraint
43 , dropConstraint
@@ -57,17 +51,6 @@ module Squeal.PostgreSQL.Definition.Table
57 , setNotNull
51 , setNotNull
58 , dropNotNull
52 , dropNotNull
59 , alterType
53 , alterType
61− , ColumnTypeExpression (..)
73import Control.DeepSeq
56import Control.DeepSeq
@@ -78,9 +61,9 @@ import qualified Generics.SOP as SOP
78import qualified GHC.Generics as GHC
61import qualified GHC.Generics as GHC
80import Squeal.PostgreSQL.Alias
63import Squeal.PostgreSQL.Alias
81−import Squeal.PostgreSQL.Definition (Definition (..))
64+import Squeal.PostgreSQL.Definition
65+import Squeal.PostgreSQL.Definition.Table.Constraint
82import Squeal.PostgreSQL.Expression
66import Squeal.PostgreSQL.Expression
83−import Squeal.PostgreSQL.Expression.Logic
84import Squeal.PostgreSQL.Expression.Type
67import Squeal.PostgreSQL.Expression.Type
85import Squeal.PostgreSQL.List
68import Squeal.PostgreSQL.List
86import Squeal.PostgreSQL.Render
69import Squeal.PostgreSQL.Render
@@ -195,288 +178,6 @@ renderCreation tab columns constraints = renderSQL tab
195 renderConstraint (constraint `As` alias) =
178 renderConstraint (constraint `As` alias) =
196 "CONSTRAINT" <+> renderSQL alias <+> renderSQL constraint
179 "CONSTRAINT" <+> renderSQL alias <+> renderSQL constraint
198−-- | Data types are a way to limit the kind of data that can be stored in a
199−-- table. For many applications, however, the constraint they provide is
200−-- too coarse. For example, a column containing a product price should
201−-- probably only accept positive values. But there is no standard data type
202−-- that accepts only positive numbers. Another issue is that you might want
203−-- to constrain column data with respect to other columns or rows.
204−-- For example, in a table containing product information,
205−-- there should be only one row for each product number.
206−-- `TableConstraint`s give you as much control over the data in your tables
207−-- as you wish. If a user attempts to store data in a column that would
208−-- violate a constraint, an error is raised. This applies
209−-- even if the value came from the default value definition.
210−newtype TableConstraintExpression
213− (schemas :: SchemasType)
214− (constraint :: TableConstraint)
215− = UnsafeTableConstraintExpression
216− { renderTableConstraintExpression :: ByteString }
217− deriving (GHC.Generic,Show,Eq,Ord,NFData)
219− (TableConstraintExpression sch tab schemas constraint) where
220− renderSQL = renderTableConstraintExpression
222−{-| A `check` constraint is the most generic `TableConstraint` type.
223−It allows you to specify that the value in a certain column must satisfy
224−a Boolean (truth-value) expression.
228− "tab" ::: 'Table ('[ "inequality" ::: 'Check '["a","b"]] :=> '[
229− "a" ::: 'NoDef :=> 'NotNull 'PGint4,
230− "b" ::: 'NoDef :=> 'NotNull 'PGint4
236− definition :: Definition (Public '[]) (Public Schema)
237− definition = createTable #tab
238− ( (int & notNullable) `as` #a :*
239− (int & notNullable) `as` #b )
240− ( check (#a :* #b) (#a .> #b) `as` #inequality )
243−>>> printSQL definition
244−CREATE TABLE "tab" ("a" int NOT NULL, "b" int NOT NULL, CONSTRAINT "inequality" CHECK (("a" > "b")));
247− :: ( Has sch schemas schema
248− , Has tab schema ('Table table)
249− , HasAll aliases (TableToRow table) subcolumns )
251− -- ^ specify the subcolumns which are getting checked
252− -> (forall t. Condition '[] '[] 'Ungrouped schemas '[] '[t ::: subcolumns])
253− -- ^ a closed `Condition` on those subcolumns
254− -> TableConstraintExpression sch tab schemas ('Check aliases)
255−check _cols condition = UnsafeTableConstraintExpression $
256− "CHECK" <+> parenthesized (renderSQL condition)
258−{-| A `unique` constraint ensure that the data contained in a column,
259−or a group of columns, is unique among all the rows in the table.
263− "tab" ::: 'Table( '[ "uq_a_b" ::: 'Unique '["a","b"]] :=> '[
264− "a" ::: 'NoDef :=> 'Null 'PGint4,
265− "b" ::: 'NoDef :=> 'Null 'PGint4
271− definition :: Definition (Public '[]) (Public Schema)
272− definition = createTable #tab
273− ( (int & nullable) `as` #a :*
274− (int & nullable) `as` #b )
275− ( unique (#a :* #b) `as` #uq_a_b )
278−>>> printSQL definition
279−CREATE TABLE "tab" ("a" int NULL, "b" int NULL, CONSTRAINT "uq_a_b" UNIQUE ("a", "b"));
282− :: ( Has sch schemas schema
283− , Has tab schema('Table table)
284− , HasAll aliases (TableToRow table) subcolumns )
286− -- ^ specify subcolumns which together are unique for each row
287− -> TableConstraintExpression sch tab schemas ('Unique aliases)
288−unique columns = UnsafeTableConstraintExpression $
289− "UNIQUE" <+> parenthesized (renderSQL columns)
291−{-| A `primaryKey` constraint indicates that a column, or group of columns,
292−can be used as a unique identifier for rows in the table.
293−This requires that the values be both unique and not null.
297− "tab" ::: 'Table ('[ "pk_id" ::: 'PrimaryKey '["id"]] :=> '[
298− "id" ::: 'Def :=> 'NotNull 'PGint4,
299− "name" ::: 'NoDef :=> 'NotNull 'PGtext
305− definition :: Definition (Public '[]) (Public Schema)
306− definition = createTable #tab
307− ( serial `as` #id :*
308− (text & notNullable) `as` #name )
309− ( primaryKey #id `as` #pk_id )
312−>>> printSQL definition
313−CREATE TABLE "tab" ("id" serial, "name" text NOT NULL, CONSTRAINT "pk_id" PRIMARY KEY ("id"));
316− :: ( Has sch schemas schema
317− , Has tab schema ('Table table)
318− , HasAll aliases (TableToColumns table) subcolumns
319− , AllNotNull subcolumns )
321− -- ^ specify the subcolumns which together form a primary key.
322− -> TableConstraintExpression sch tab schemas ('PrimaryKey aliases)
323−primaryKey columns = UnsafeTableConstraintExpression $
324− "PRIMARY KEY" <+> parenthesized (renderSQL columns)
326−{-| A `foreignKey` specifies that the values in a column
327−(or a group of columns) must match the values appearing in some row of
328−another table. We say this maintains the referential integrity
329−between two related tables.
333− '[ "users" ::: 'Table (
334− '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
335− '[ "id" ::: 'Def :=> 'NotNull 'PGint4
336− , "name" ::: 'NoDef :=> 'NotNull 'PGtext
338− , "emails" ::: 'Table (
339− '[ "pk_emails" ::: 'PrimaryKey '["id"]
340− , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
342− '[ "id" ::: 'Def :=> 'NotNull 'PGint4
343− , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
344− , "email" ::: 'NoDef :=> 'Null 'PGtext
351− setup :: Definition (Public '[]) (Public Schema)
354− ( serial `as` #id :*
355− (text & notNullable) `as` #name )
356− ( primaryKey #id `as` #pk_users ) >>>
358− ( serial `as` #id :*
359− (int & notNullable) `as` #user_id :*
360− (text & nullable) `as` #email )
361− ( primaryKey #id `as` #pk_emails :*
362− foreignKey #user_id #users #id
363− OnDeleteCascade OnUpdateCascade `as` #fk_user_id )
366−CREATE TABLE "users" ("id" serial, "name" text NOT NULL, CONSTRAINT "pk_users" PRIMARY KEY ("id"));
367−CREATE TABLE "emails" ("id" serial, "user_id" int NOT NULL, "email" text NULL, CONSTRAINT "pk_emails" PRIMARY KEY ("id"), CONSTRAINT "fk_user_id" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE);
369−A `foreignKey` can even be a table self-reference.
373− '[ "employees" ::: 'Table (
374− '[ "employees_pk" ::: 'PrimaryKey '["id"]
375− , "employees_employer_fk" ::: 'ForeignKey '["employer_id"] "employees" '["id"]
377− '[ "id" ::: 'Def :=> 'NotNull 'PGint4
378− , "name" ::: 'NoDef :=> 'NotNull 'PGtext
379− , "employer_id" ::: 'NoDef :=> 'Null 'PGint4
386− setup :: Definition (Public '[]) (Public Schema)
388− createTable #employees
389− ( serial `as` #id :*
390− (text & notNullable) `as` #name :*
391− (integer & nullable) `as` #employer_id )
392− ( primaryKey #id `as` #employees_pk :*
393− foreignKey #employer_id #employees #id
394− OnDeleteCascade OnUpdateCascade `as` #employees_employer_fk )
397−CREATE TABLE "employees" ("id" serial, "name" text NOT NULL, "employer_id" integer NULL, CONSTRAINT "employees_pk" PRIMARY KEY ("id"), CONSTRAINT "employees_employer_fk" FOREIGN KEY ("employer_id") REFERENCES "employees" ("id") ON DELETE CASCADE ON UPDATE CASCADE);
400− :: (ForeignKeyed schemas sch schema child parent
406− -- ^ column or columns in the table
408− -- ^ reference table
409− -> NP Alias refcolumns
410− -- ^ reference column or columns in the reference table
412− -- ^ what to do when reference is deleted
414− -- ^ what to do when reference is updated
415− -> TableConstraintExpression sch child schemas
416− ('ForeignKey columns parent refcolumns)
417−foreignKey keys parent refs ondel onupd = UnsafeTableConstraintExpression $
418− "FOREIGN KEY" <+> parenthesized (renderSQL keys)
419− <+> "REFERENCES" <+> renderSQL parent
420− <+> parenthesized (renderSQL refs)
424−-- | A constraint synonym between types involved in a foreign key constraint.
425−type ForeignKeyed schemas
433− ( Has sch schemas schema
434− , Has child schema ('Table table)
435− , Has parent schema ('Table reftable)
436− , HasAll columns (TableToColumns table) tys
437− , reftable ~ (constraints :=> cols)
438− , HasAll refcolumns cols reftys
439− , SOP.AllZip SamePGType tys reftys
440− , Uniquely refcolumns constraints )
442−-- | `OnDeleteClause` indicates what to do with rows that reference a deleted row.
445− -- ^ if any referencing rows still exist when the constraint is checked,
446− -- an error is raised
447− | OnDeleteRestrict -- ^ prevents deletion of a referenced row
449− -- ^ specifies that when a referenced row is deleted,
450− -- row(s) referencing it should be automatically deleted as well
451− deriving (GHC.Generic,Show,Eq,Ord)
452−instance NFData OnDeleteClause
453−-- | Render `OnDeleteClause`.
454−instance RenderSQL OnDeleteClause where
456− OnDeleteNoAction -> "ON DELETE NO ACTION"
457− OnDeleteRestrict -> "ON DELETE RESTRICT"
458− OnDeleteCascade -> "ON DELETE CASCADE"
460−-- | Analagous to `OnDeleteClause` there is also `OnUpdateClause` which is invoked
461−-- when a referenced column is changed (updated).
464− -- ^ if any referencing rows has not changed when the constraint is checked,
465− -- an error is raised
466− | OnUpdateRestrict -- ^ prevents update of a referenced row
468− -- ^ the updated values of the referenced column(s) should be copied
469− -- into the referencing row(s)
470− deriving (GHC.Generic,Show,Eq,Ord)
471−instance NFData OnUpdateClause
473−-- | Render `OnUpdateClause`.
474−instance RenderSQL OnUpdateClause where
476− OnUpdateNoAction -> "ON UPDATE NO ACTION"
477− OnUpdateRestrict -> "ON UPDATE RESTRICT"
478− OnUpdateCascade -> "ON UPDATE CASCADE"
480-- | `dropTable` removes a table from the schema.
181-- | `dropTable` removes a table from the schema.
@@ -496,35 +197,56 @@ dropTable tab = UnsafeDefinition $ "DROP TABLE" <+> renderSQL tab <> ";"
497dropTableIfExists
198dropTableIfExists
498 :: ( Has sch schemas schema
199 :: ( Has sch schemas schema
499− , Has tab schema ('Table table))
200+ , KnownSymbol tab)
500 => QualifiedAlias sch tab -- ^ table to remove
201 => QualifiedAlias sch tab -- ^ table to remove
501− -> Definition schemas (Alter sch (DropIfExists tab schema) schemas)
202+ -> Definition schemas (Alter sch (DropSchemumIfExists tab 'Table schema) schemas)
502dropTableIfExists tab = UnsafeDefinition $ "DROP TABLE IF EXISTS" <+> renderSQL tab <> ";"
203dropTableIfExists tab = UnsafeDefinition $ "DROP TABLE IF EXISTS" <+> renderSQL tab <> ";"
504-- | `alterTable` changes the definition of a table from the schema.
205-- | `alterTable` changes the definition of a table from the schema.
505alterTable
206alterTable
506− :: (Has sch schemas schema, Has tab schema ('Table table0))
207+ :: (Has sch schemas schema, KnownSymbol tab)
507 => QualifiedAlias sch tab -- ^ table to alter
208 => QualifiedAlias sch tab -- ^ table to alter
508− -> AlterTable sch tab schemas table1 -- ^ alteration to perform
209+ -> AlterTable sch tab schemas table -- ^ alteration to perform
509− -> Definition schemas (Alter sch (Alter tab ('Table table1) schema) schemas)
210+ -> Definition schemas (Alter sch (Alter tab ('Table table) schema) schemas)
510alterTable tab alteration = UnsafeDefinition $
211alterTable tab alteration = UnsafeDefinition $
511 "ALTER TABLE"
212 "ALTER TABLE"
512 <+> renderSQL tab
213 <+> renderSQL tab
513 <+> renderAlterTable alteration
214 <+> renderAlterTable alteration
217+-- | `alterTable` changes the definition of a table from the schema.
219+ :: (Has sch schemas schema, KnownSymbol tab)
220+ => QualifiedAlias sch tab -- ^ table to alter
221+ -> AlterTable sch tab schemas table -- ^ alteration to perform
222+ -> Definition schemas (Alter sch (AlterIfExists tab ('Table table) schema) schemas)
223+alterTableIfExists tab alteration = UnsafeDefinition $
224+ "ALTER TABLE IF EXISTS"
226+ <+> renderAlterTable alteration
516-- | `alterTableRename` changes the name of a table from the schema.
229-- | `alterTableRename` changes the name of a table from the schema.
518-- >>> printSQL $ alterTableRename #foo #bar
231-- >>> printSQL $ alterTableRename #foo #bar
519-- ALTER TABLE "foo" RENAME TO "bar";
232-- ALTER TABLE "foo" RENAME TO "bar";
520alterTableRename
233alterTableRename
521− :: (KnownSymbol table0, KnownSymbol table1)
234+ :: (KnownSymbol tab0, KnownSymbol tab1)
522− => Alias table0 -- ^ table to rename
235+ => Alias tab0 -- ^ table to rename
523− -> Alias table1 -- ^ what to rename it
236+ -> Alias tab1 -- ^ what to rename it
524− -> Definition schema (Rename table0 table1 schema)
237+ -> Definition schema (Rename tab0 tab1 schema)
525−alterTableRename table0 table1 = UnsafeDefinition $
238+alterTableRename tab0 tab1 = UnsafeDefinition $
239+ "ALTER TABLE" <+> renderSQL tab0
240+ <+> "RENAME TO" <+> renderSQL tab1 <> ";"
242+alterTableIfExistsRename
243+ :: (KnownSymbol tab0, KnownSymbol tab1)
244+ => Alias tab0 -- ^ table to rename
245+ -> Alias tab1 -- ^ what to rename it
246+ -> Definition schema (RenameIfExists tab0 tab1 schema)
247+alterTableIfExistsRename tab0 tab1 = UnsafeDefinition $
526− "ALTER TABLE" <+> renderSQL table0
248+ "ALTER TABLE IF EXISTS" <+> renderSQL tab0
527− <+> "RENAME TO" <+> renderSQL table1 <> ";"
249+ <+> "RENAME TO" <+> renderSQL tab1 <> ";"
529-- | An `AlterTable` describes the alteration to perform on the columns
251-- | An `AlterTable` describes the alteration to perform on the columns
530-- of a table.
252-- of a table.
@@ -776,51 +498,3 @@ dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
776-- ALTER TABLE "tab" ALTER COLUMN "col" TYPE numeric NOT NULL;
498-- ALTER TABLE "tab" ALTER COLUMN "col" TYPE numeric NOT NULL;
777alterType :: ColumnTypeExpression schemas ty -> AlterColumn schemas ty0 ty
499alterType :: ColumnTypeExpression schemas ty -> AlterColumn schemas ty0 ty
778alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
500alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
780−-- | `ColumnTypeExpression`s are used in `createTable` commands.
781−newtype ColumnTypeExpression (schemas :: SchemasType) (ty :: ColumnType)
782− = UnsafeColumnTypeExpression { renderColumnTypeExpression :: ByteString }
783− deriving (GHC.Generic,Show,Eq,Ord,NFData)
784−instance RenderSQL (ColumnTypeExpression schemas ty) where
785− renderSQL = renderColumnTypeExpression
787−-- | used in `createTable` commands as a column constraint to note that
788−-- @NULL@ may be present in a column
790− :: TypeExpression schemas (nullity ty)
791− -> ColumnTypeExpression schemas ('NoDef :=> 'Null ty)
792−nullable ty = UnsafeColumnTypeExpression $ renderSQL ty <+> "NULL"
794−-- | used in `createTable` commands as a column constraint to ensure
795−-- @NULL@ is not present in a column
797− :: TypeExpression schemas (nullity ty)
798− -> ColumnTypeExpression schemas ('NoDef :=> 'NotNull ty)
799−notNullable ty = UnsafeColumnTypeExpression $ renderSQL ty <+> "NOT NULL"
801−-- | used in `createTable` commands as a column constraint to give a default
803− :: Expression '[] '[] 'Ungrouped schemas '[] '[] ty
804− -> ColumnTypeExpression schemas ('NoDef :=> ty)
805− -> ColumnTypeExpression schemas ('Def :=> ty)
806−default_ x ty = UnsafeColumnTypeExpression $
807− renderSQL ty <+> "DEFAULT" <+> renderExpression x
809−-- | not a true type, but merely a notational convenience for creating
810−-- unique identifier columns with type `PGint2`
812− :: ColumnTypeExpression schemas ('Def :=> 'NotNull 'PGint2)
813−serial2 = UnsafeColumnTypeExpression "serial2"
814−smallserial = UnsafeColumnTypeExpression "smallserial"
815−-- | not a true type, but merely a notational convenience for creating
816−-- unique identifier columns with type `PGint4`
818− :: ColumnTypeExpression schemas ('Def :=> 'NotNull 'PGint4)
819−serial4 = UnsafeColumnTypeExpression "serial4"
820−serial = UnsafeColumnTypeExpression "serial"
821−-- | not a true type, but merely a notational convenience for creating
822−-- unique identifier columns with type `PGint8`
824− :: ColumnTypeExpression schemas ('Def :=> 'NotNull 'PGint8)
825−serial8 = UnsafeColumnTypeExpression "serial8"
826−bigserial = UnsafeColumnTypeExpression "bigserial"