@@ -66,14 +66,15 @@ module Squeal.PostgreSQL.Definition
66 , createView
66 , createView
67 , dropView
67 , dropView
68 -- * Types
68 -- * Types
69− , createTypeComposite
70 , createTypeEnum
69 , createTypeEnum
71− , HasPGcomposite (..)
70+ , createTypeEnumWith
72− , HasPGenum (..)
71+ , createTypeComposite
72+ , createTypeCompositeWith
73 , dropType
73 , dropType
74 , ColumnTypeExpression (..)
75 , ColumnTypeExpression (..)
75− , hasNull
76+ , nullable
76− , hasNotNull
77+ , notNullable
77 , default_
78 , default_
79 , smallserial
80 , smallserial
@@ -125,7 +126,7 @@ CREATE statements
125-- >>> :set -XOverloadedLabels
126-- >>> :set -XOverloadedLabels
127-- renderDefinition $
128-- renderDefinition $
128−-- createTable #tab ((int & hasNull) `As` #a :* (real & hasNull) `As` #b :* Nil) Nil
129+-- createTable #tab ((int & nullable) `As` #a :* (real & nullable) `As` #b :* Nil) Nil
130-- "CREATE TABLE \"tab\" (\"a\" int NULL, \"b\" real NULL);"
131-- "CREATE TABLE \"tab\" (\"a\" int NULL, \"b\" real NULL);"
131createTable
132createTable
@@ -152,7 +153,7 @@ createTable tab columns constraints = UnsafeDefinition $
152-- >>> type Schema = '["tab" ::: 'Table Table]
153-- >>> type Schema = '["tab" ::: 'Table Table]
154-- renderDefinition
155-- renderDefinition
155−-- (createTableIfNotExists #tab ((int & hasNull) `As` #a :* (real & hasNull) `As` #b :* Nil) Nil :: Definition Schema Schema)
156+-- (createTableIfNotExists #tab ((int & nullable) `As` #a :* (real & nullable) `As` #b :* Nil) Nil :: Definition Schema Schema)
157-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int NULL, \"b\" real NULL);"
158-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int NULL, \"b\" real NULL);"
158createTableIfNotExists
159createTableIfNotExists
@@ -234,8 +235,8 @@ newtype TableConstraintExpression
235-- definition :: Definition '[] Schema
236-- definition :: Definition '[] Schema
236-- definition = createTable #tab
237-- definition = createTable #tab
237−-- ( (int & hasNotNull) `As` #a :*
238+-- ( (int & notNullable) `As` #a :*
238−-- (int & hasNotNull) `As` #b :* Nil )
239+-- (int & notNullable) `As` #b :* Nil )
239-- ( check (#a :* #b :* Nil) (#a .> #b) `As` #inequality :* Nil )
240-- ( check (#a :* #b :* Nil) (#a .> #b) `As` #inequality :* Nil )
@@ -265,8 +266,8 @@ check _cols condition = UnsafeTableConstraintExpression $
266-- definition :: Definition '[] Schema
267-- definition :: Definition '[] Schema
267-- definition = createTable #tab
268-- definition = createTable #tab
268−-- ( (int & hasNull) `As` #a :*
269+-- ( (int & nullable) `As` #a :*
269−-- (int & hasNull) `As` #b :* Nil )
270+-- (int & nullable) `As` #b :* Nil )
270-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
271-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
@@ -297,7 +298,7 @@ unique columns = UnsafeTableConstraintExpression $
297-- definition :: Definition '[] Schema
298-- definition :: Definition '[] Schema
298-- definition = createTable #tab
299-- definition = createTable #tab
299-- ( serial `As` #id :*
300-- ( serial `As` #id :*
300−-- (text & hasNotNull) `As` #name :* Nil )
301+-- (text & notNullable) `As` #name :* Nil )
301-- ( primaryKey #id `As` #pk_id :* Nil )
302-- ( primaryKey #id `As` #pk_id :* Nil )
@@ -341,12 +342,12 @@ primaryKey columns = UnsafeTableConstraintExpression $
341-- setup =
342-- setup =
342-- createTable #users
343-- createTable #users
343-- ( serial `As` #id :*
344-- ( serial `As` #id :*
344−-- (text & hasNotNull) `As` #name :* Nil )
345+-- (text & notNullable) `As` #name :* Nil )
345-- ( primaryKey #id `As` #pk_users :* Nil ) >>>
346-- ( primaryKey #id `As` #pk_users :* Nil ) >>>
346-- createTable #emails
347-- createTable #emails
347-- ( serial `As` #id :*
348-- ( serial `As` #id :*
348−-- (int & hasNotNull) `As` #user_id :*
349+-- (int & notNullable) `As` #user_id :*
349−-- (text & hasNull) `As` #email :* Nil )
350+-- (text & nullable) `As` #email :* Nil )
350-- ( primaryKey #id `As` #pk_emails :*
351-- ( primaryKey #id `As` #pk_emails :*
351-- foreignKey #user_id #users #id
352-- foreignKey #user_id #users #id
352-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
353-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
@@ -375,8 +376,8 @@ primaryKey columns = UnsafeTableConstraintExpression $
375-- setup =
376-- setup =
376-- createTable #employees
377-- createTable #employees
377-- ( serial `As` #id :*
378-- ( serial `As` #id :*
378−-- (text & hasNotNull) `As` #name :*
379+-- (text & notNullable) `As` #name :*
379−-- (integer & hasNull) `As` #employer_id :* Nil )
380+-- (integer & nullable) `As` #employer_id :* Nil )
380-- ( primaryKey #id `As` #employees_pk :*
381-- ( primaryKey #id `As` #employees_pk :*
381-- foreignKey #employer_id #employees #id
382-- foreignKey #employer_id #employees #id
382-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
383-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
@@ -409,6 +410,7 @@ foreignKey keys parent refs ondel onupd = UnsafeTableConstraintExpression $
409 <+> renderOnDeleteClause ondel
410 <+> renderOnDeleteClause ondel
410 <+> renderOnUpdateClause onupd
411 <+> renderOnUpdateClause onupd
413+-- | A constraint synonym between types involved in a foreign key constraint.
412type ForeignKeyed schema
414type ForeignKeyed schema
413 child parent
415 child parent
414 table reftable
416 table reftable
@@ -578,7 +580,7 @@ class AddColumn ty where
578 -- '["tab" ::: 'Table ('[] :=>
580 -- '["tab" ::: 'Table ('[] :=>
579 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
581 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
580 -- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
582 -- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
581− -- definition = alterTable #tab (addColumn #col2 (text & hasNull & default_ "foo"))
583+ -- definition = alterTable #tab (addColumn #col2 (text & nullable & default_ "foo"))
582 -- in renderDefinition definition
584 -- in renderDefinition definition
584 -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL DEFAULT E'foo';"
586 -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL DEFAULT E'foo';"
@@ -590,7 +592,7 @@ class AddColumn ty where
590 -- '["tab" ::: 'Table ('[] :=>
592 -- '["tab" ::: 'Table ('[] :=>
591 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
593 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
592 -- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
594 -- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
593− -- definition = alterTable #tab (addColumn #col2 (text & hasNull))
595+ -- definition = alterTable #tab (addColumn #col2 (text & nullable))
594 -- in renderDefinition definition
596 -- in renderDefinition definition
596 -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL;"
598 -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL;"
@@ -749,14 +751,15 @@ dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
749-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
751-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
750-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGnumeric])]
752-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGnumeric])]
751-- definition =
753-- definition =
752−-- alterTable #tab (alterColumn #col (alterType (numeric & hasNotNull)))
754+-- alterTable #tab (alterColumn #col (alterType (numeric & notNullable)))
753-- in renderDefinition definition
755-- in renderDefinition definition
755-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" TYPE numeric NOT NULL;"
757-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" TYPE numeric NOT NULL;"
756alterType :: ColumnTypeExpression schema ty -> AlterColumn schema ty0 ty
758alterType :: ColumnTypeExpression schema ty -> AlterColumn schema ty0 ty
757alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
759alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
759−-- | create a view...
761+-- | Create a view.
762-- definition :: Definition
765-- definition :: Definition
@@ -778,12 +781,28 @@ createView alias query = UnsafeDefinition $
778 "CREATE" <+> "VIEW" <+> renderAlias alias <+> "AS"
781 "CREATE" <+> "VIEW" <+> renderAlias alias <+> "AS"
779 <+> renderQuery query <> ";"
782 <+> renderQuery query <> ";"
788+-- definition :: Definition
789+-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])
790+-- , "bc" ::: 'View ('["b" ::: 'Null 'PGint4, "c" ::: 'Null 'PGint4])]
791+-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])]
792+-- definition = dropView #bc
793+-- in renderDefinition definition
795+-- "DROP VIEW \"bc\";"
782 :: Has view schema ('View v)
797 :: Has view schema ('View v)
783 => Alias view -- ^ view to remove
798 => Alias view -- ^ view to remove
784 -> Definition schema (Drop view schema)
799 -> Definition schema (Drop view schema)
785dropView v = UnsafeDefinition $ "DROP VIEW" <+> renderAlias v <> ";"
800dropView v = UnsafeDefinition $ "DROP VIEW" <+> renderAlias v <> ";"
802+-- | Enumerated types are created using the `createTypeEnum` command, for example
804+-- >>> renderDefinition $ createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy" :* Nil)
805+-- "CREATE TYPE \"mood\" AS ('sad', 'ok', 'happy');"
787createTypeEnum
806createTypeEnum
788 :: (KnownSymbol enum, SOP.All KnownSymbol labels)
807 :: (KnownSymbol enum, SOP.All KnownSymbol labels)
789 => Alias enum
808 => Alias enum
@@ -793,17 +812,29 @@ createTypeEnum enum labels = UnsafeDefinition $
793 "CREATE" <+> "TYPE" <+> renderAlias enum <+> "AS" <+>
812 "CREATE" <+> "TYPE" <+> renderAlias enum <+> "AS" <+>
794 parenthesized (commaSeparated (renderLabels labels)) <> ";"
813 parenthesized (commaSeparated (renderLabels labels)) <> ";"
796−class
815+-- | Enumerated types can also be generated from a Haskell type, for example
817+-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
818+-- >>> instance SOP.Generic Schwarma
819+-- >>> instance SOP.HasDatatypeInfo Schwarma
820+-- >>> renderDefinition $ createTypeEnumWith @Schwarma #schwarma
821+-- "CREATE TYPE \"schwarma\" AS ('Beef', 'Lamb', 'Chicken');"
823+ :: forall hask enum schema.
797− ( SOP.IsEnumType hask
824+ ( SOP.Generic hask
798 , SOP.All KnownSymbol (LabelsWith hask)
825 , SOP.All KnownSymbol (LabelsWith hask)
799− ) => HasPGenum hask where
801− :: KnownSymbol enum
826+ , KnownSymbol enum
802− => Alias enum
828+ => Alias enum
803− -> Definition schema (Create enum ('Typedef (EnumWith hask)) schema)
829+ -> Definition schema (Create enum ('Typedef (EnumWith hask)) schema)
804− createTypeEnumWith enum = createTypeEnum enum
830+createTypeEnumWith enum = createTypeEnum enum
805− (SOP.hpure label :: NP PGlabel (LabelsWith hask))
831+ (SOP.hpure label :: NP PGlabel (LabelsWith hask))
833+-- | `createTypeComposite` creates a composite type. The composite type is
834+-- specified by a list of attribute names and data types.
836+-- >>> renderDefinition $ createTypeComposite #complex (float8 `As` #real :* float8 `As` #imaginary :* Nil)
837+-- "CREATE TYPE \"complex\" AS (float8 AS \"real\", float8 AS \"imaginary\");"
807createTypeComposite
838createTypeComposite
808 :: (KnownSymbol ty, SOP.SListI fields)
839 :: (KnownSymbol ty, SOP.SListI fields)
809 => Alias ty
840 => Alias ty
@@ -813,19 +844,33 @@ createTypeComposite ty fields = UnsafeDefinition $
813 "CREATE" <+> "TYPE" <+> renderAlias ty <+> "AS" <+> parenthesized
844 "CREATE" <+> "TYPE" <+> renderAlias ty <+> "AS" <+> parenthesized
814 (renderCommaSeparated (renderAliasedAs renderTypeExpression) fields) <> ";"
845 (renderCommaSeparated (renderAliasedAs renderTypeExpression) fields) <> ";"
816−class
847+-- | Composite types can also be generated from a Haskell type, for example
849+-- >>> data Complex = Complex {real :: Maybe Double, imaginary :: Maybe Double} deriving GHC.Generic
850+-- >>> instance SOP.Generic Complex
851+-- >>> instance SOP.HasDatatypeInfo Complex
852+-- >>> renderDefinition $ createTypeCompositeWith @Complex #complex
853+-- "CREATE TYPE \"complex\" AS (float8 AS \"real\", float8 AS \"imaginary\");"
854+createTypeCompositeWith
855+ :: forall hask ty schema.
817 ( ZipAliased (FieldNamesWith hask) (FieldTypesWith hask)
856 ( ZipAliased (FieldNamesWith hask) (FieldTypesWith hask)
818 , SOP.All PGTyped (FieldTypesWith hask)
857 , SOP.All PGTyped (FieldTypesWith hask)
819− ) => HasPGcomposite hask where
820− createTypeCompositeWith
821− :: KnownSymbol ty
858+ , KnownSymbol ty
822− => Alias ty
860+ => Alias ty
823− -> Definition schema (Create ty ( 'Typedef (CompositeWith hask)) schema)
861+ -> Definition schema (Create ty ( 'Typedef (CompositeWith hask)) schema)
824− createTypeCompositeWith ty = createTypeComposite ty $ zipAs
862+createTypeCompositeWith ty = createTypeComposite ty $ zipAs
825− (SOP.hpure Alias :: NP Alias (FieldNamesWith hask))
863+ (SOP.hpure Alias :: NP Alias (FieldNamesWith hask))
826− (SOP.hcpure (SOP.Proxy :: SOP.Proxy PGTyped) pgtype
864+ (SOP.hcpure (SOP.Proxy :: SOP.Proxy PGTyped) pgtype
827− :: NP TypeExpression (FieldTypesWith hask))
865+ :: NP TypeExpression (FieldTypesWith hask))
869+-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
870+-- >>> instance SOP.Generic Schwarma
871+-- >>> instance SOP.HasDatatypeInfo Schwarma
872+-- >>> renderDefinition (dropType #schwarma :: Definition '["schwarma" ::: 'Typedef (EnumWith Schwarma)] '[])
873+-- "DROP TYPE \"schwarma\";"
830 :: Has tydef schema ('Typedef ty)
875 :: Has tydef schema ('Typedef ty)
831 => Alias tydef
876 => Alias tydef
@@ -841,17 +886,19 @@ instance (Has alias schema ('Typedef ty))
841 => IsLabel alias (ColumnTypeExpression schema ('NoDef :=> 'NotNull ty)) where
886 => IsLabel alias (ColumnTypeExpression schema ('NoDef :=> 'NotNull ty)) where
842 fromLabel = UnsafeColumnTypeExpression (renderAlias (fromLabel @alias))
887 fromLabel = UnsafeColumnTypeExpression (renderAlias (fromLabel @alias))
844−hasNull
889+-- | used in `createTable` commands as a column constraint to note that
890+-- @NULL@ may be present in a column
845 :: TypeExpression ty
892 :: TypeExpression ty
846 -> ColumnTypeExpression schema ('NoDef :=> 'Null ty)
893 -> ColumnTypeExpression schema ('NoDef :=> 'Null ty)
847−hasNull ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NULL"
894+nullable ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NULL"
849-- | used in `createTable` commands as a column constraint to ensure
896-- | used in `createTable` commands as a column constraint to ensure
850−-- @NULL@ is not present
897+-- @NULL@ is not present in a column
851−hasNotNull
898+notNullable
852 :: TypeExpression ty
899 :: TypeExpression ty
853 -> ColumnTypeExpression schema (def :=> 'NotNull ty)
900 -> ColumnTypeExpression schema (def :=> 'NotNull ty)
854−hasNotNull ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
901+notNullable ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
856-- | used in `createTable` commands as a column constraint to give a default
903-- | used in `createTable` commands as a column constraint to give a default