@@ -67,6 +67,17 @@ module Squeal.PostgreSQL.Definition
6767 -- * Views
6868 , createView
6969 , dropView
70+ , createTypeEnum
71+ , ColumnTypeExpression (..)
72+ , notNull
73+ , null'
74+ , default_
75+ , serial2
76+ , smallserial
77+ , serial4
78+ , serial
79+ , serial8
80+ , bigserial
7081 ) where
7182
7283import Control.Category
@@ -111,9 +122,9 @@ CREATE statements
111122-- >>> :set -XOverloadedLabels
112123-- >>> :{
113124-- renderDefinition $
114−-- createTable #tab (int `As` #a :* real `As` #b :* Nil) Nil
125+-- createTable #tab ((int & null') `As` #a :* (real & null') `As` #b :* Nil) Nil
115126-- :}
116−-- "CREATE TABLE \"tab\" (\"a\" int, \"b\" real);"
127+-- "CREATE TABLE \"tab\" (\"a\" int NULL, \"b\" real NULL);"
117128createTable
118129 :: ( KnownSymbol table
119130 , columns ~ (col ': cols)
@@ -121,7 +132,7 @@ createTable
121132 , SOP.SListI constraints
122133 , schema1 ~ Create table ('Table (constraints :=> columns)) schema0 )
123134 => Alias table -- ^ the name of the table to add
124− -> NP (Aliased TypeExpression) columns
135+ -> NP (Aliased (ColumnTypeExpression schema0)) columns
125136 -- ^ the names and datatype of each column
126137 -> NP (Aliased (TableConstraintExpression schema1 table)) constraints
127138 -- ^ constraints that must hold for the table
@@ -138,15 +149,15 @@ createTable tab columns constraints = UnsafeDefinition $
138149-- >>> type Schema = '["tab" ::: 'Table Table]
139150-- >>> :{
140151-- renderDefinition
141−-- (createTableIfNotExists #tab (int `As` #a :* real `As` #b :* Nil) Nil :: Definition Schema Schema)
152+-- (createTableIfNotExists #tab ((int & null') `As` #a :* (real & null') `As` #b :* Nil) Nil :: Definition Schema Schema)
142153-- :}
143−-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int, \"b\" real);"
154+-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int NULL, \"b\" real NULL);"
144155createTableIfNotExists
145156 :: ( Has table schema ('Table (constraints :=> columns))
146157 , SOP.SListI columns
147158 , SOP.SListI constraints )
148159 => Alias table -- ^ the name of the table to add
149− -> NP (Aliased TypeExpression) columns
160+ -> NP (Aliased (ColumnTypeExpression schema)) columns
150161 -- ^ the names and datatype of each column
151162 -> NP (Aliased (TableConstraintExpression schema table)) constraints
152163 -- ^ constraints that must hold for the table
@@ -161,9 +172,9 @@ renderCreation
161172 , SOP.SListI columns
162173 , SOP.SListI constraints )
163174 => Alias table -- ^ the name of the table to add
164− -> NP (Aliased TypeExpression) columns
175+ -> NP (Aliased (ColumnTypeExpression schema0)) columns
165176 -- ^ the names and datatype of each column
166− -> NP (Aliased (TableConstraintExpression schema table)) constraints
177+ -> NP (Aliased (TableConstraintExpression schema1 table)) constraints
167178 -- ^ constraints that must hold for the table
168179 -> ByteString
169180renderCreation tab columns constraints = renderAlias tab
@@ -175,9 +186,9 @@ renderCreation tab columns constraints = renderAlias tab
175186 renderCommaSeparated renderConstraint constraints ) )
176187 <> ";"
177188 where
178− renderColumnDef :: Aliased TypeExpression x -> ByteString
189+ renderColumnDef :: Aliased (ColumnTypeExpression schema) x -> ByteString
179190 renderColumnDef (ty `As` column) =
180− renderAlias column <+> renderTypeExpression ty
191+ renderAlias column <+> renderColumnTypeExpression ty
181192 renderConstraint
182193 :: Aliased (TableConstraintExpression schema columns) constraint
183194 -> ByteString
@@ -251,13 +262,13 @@ check _cols condition = UnsafeTableConstraintExpression $
251262-- let
252263-- definition :: Definition '[] Schema
253264-- definition = createTable #tab
254−-- ( int `As` #a :*
255−-- int `As` #b :* Nil )
265+-- ( (int & null') `As` #a :*
266+-- (int & null') `As` #b :* Nil )
256267-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
257268-- :}
258269--
259270-- >>> renderDefinition definition
260−-- "CREATE TABLE \"tab\" (\"a\" int, \"b\" int, CONSTRAINT \"uq_a_b\" UNIQUE (\"a\", \"b\"));"
271+-- "CREATE TABLE \"tab\" (\"a\" int NULL, \"b\" int NULL, CONSTRAINT \"uq_a_b\" UNIQUE (\"a\", \"b\"));"
261272unique
262273 :: ( Has alias schema ('Table table)
263274 , HasAll aliases (TableToColumns table) subcolumns )
@@ -332,13 +343,13 @@ primaryKey columns = UnsafeTableConstraintExpression $
332343-- createTable #emails
333344-- ( serial `As` #id :*
334345-- (int & notNull) `As` #user_id :*
335−-- text `As` #email :* Nil )
346+-- (text & null') `As` #email :* Nil )
336347-- ( primaryKey #id `As` #pk_emails :*
337348-- foreignKey #user_id #users #id
338349-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
339350-- in renderDefinition setup
340351-- :}
341−-- "CREATE TABLE \"users\" (\"id\" serial, \"name\" text NOT NULL, CONSTRAINT \"pk_users\" PRIMARY KEY (\"id\")); CREATE TABLE \"emails\" (\"id\" serial, \"user_id\" int NOT NULL, \"email\" text, CONSTRAINT \"pk_emails\" PRIMARY KEY (\"id\"), CONSTRAINT \"fk_user_id\" FOREIGN KEY (\"user_id\") REFERENCES \"users\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE);"
352+-- "CREATE TABLE \"users\" (\"id\" serial, \"name\" text NOT NULL, CONSTRAINT \"pk_users\" PRIMARY KEY (\"id\")); 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);"
342353--
343354-- A `foreignKey` can even be a table self-reference.
344355--
@@ -362,13 +373,13 @@ primaryKey columns = UnsafeTableConstraintExpression $
362373-- createTable #employees
363374-- ( serial `As` #id :*
364375-- (text & notNull) `As` #name :*
365−-- integer `As` #employer_id :* Nil )
376+-- (integer & null') `As` #employer_id :* Nil )
366377-- ( primaryKey #id `As` #employees_pk :*
367378-- foreignKey #employer_id #employees #id
368379-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
369380-- in renderDefinition setup
370381-- :}
371−-- "CREATE TABLE \"employees\" (\"id\" serial, \"name\" text NOT NULL, \"employer_id\" integer, CONSTRAINT \"employees_pk\" PRIMARY KEY (\"id\"), CONSTRAINT \"employees_employer_fk\" FOREIGN KEY (\"employer_id\") REFERENCES \"employees\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE);"
382+-- "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);"
372383--
373384foreignKey
374385 :: (ForeignKeyed schema child parent
@@ -564,10 +575,10 @@ class AddColumn ty where
564575 -- '["tab" ::: 'Table ('[] :=>
565576 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
566577 -- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
567− -- definition = alterTable #tab (addColumn #col2 (text & default_ "foo"))
578+ -- definition = alterTable #tab (addColumn #col2 (text & null' & default_ "foo"))
568579 -- in renderDefinition definition
569580 -- :}
570− -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text DEFAULT E'foo';"
581+ -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL DEFAULT E'foo';"
571582 --
572583 -- >>> :{
573584 -- let
@@ -576,20 +587,20 @@ class AddColumn ty where
576587 -- '["tab" ::: 'Table ('[] :=>
577588 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
578589 -- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
579− -- definition = alterTable #tab (addColumn #col2 text)
590+ -- definition = alterTable #tab (addColumn #col2 (text & null'))
580591 -- in renderDefinition definition
581592 -- :}
582− -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text;"
593+ -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL;"
583594 addColumn
584595 :: ( KnownSymbol column
585596 , Has tab schema ('Table table0)
586597 , table0 ~ (constraints :=> columns)
587598 , table1 ~ (constraints :=> Create column ty columns) )
588599 => Alias column -- ^ column to add
589− -> TypeExpression ty -- ^ type of the new column
600+ -> ColumnTypeExpression schema ty -- ^ type of the new column
590601 -> AlterTable tab table1 schema
591602 addColumn column ty = UnsafeAlterTable $
592− "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
603+ "ADD COLUMN" <+> renderAlias column <+> renderColumnTypeExpression ty
593604instance {-# OVERLAPPING #-} AddColumn ('Def :=> ty)
594605instance {-# OVERLAPPABLE #-} AddColumn ('NoDef :=> 'Null ty)
595606
@@ -650,13 +661,13 @@ alterColumn
650661 , Has column columns ty0
651662 , tables1 ~ (constraints :=> Alter column ty1 columns))
652663 => Alias column -- ^ column to alter
653− -> AlterColumn ty0 ty1 -- ^ alteration to perform
664+ -> AlterColumn schema ty0 ty1 -- ^ alteration to perform
654665 -> AlterTable tab table1 schema
655666alterColumn column alteration = UnsafeAlterTable $
656667 "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
657668
658669-- | An `AlterColumn` describes the alteration to perform on a single column.
659−newtype AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
670+newtype AlterColumn (schema :: SchemaType) (ty0 :: ColumnType) (ty1 :: ColumnType) =
660671 UnsafeAlterColumn {renderAlterColumn :: ByteString}
661672 deriving (GHC.Generic,Show,Eq,Ord,NFData)
662673
@@ -675,7 +686,7 @@ newtype AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
675686-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET DEFAULT 5;"
676687setDefault
677688 :: Expression '[] 'Ungrouped '[] ty -- ^ default value to set
678− -> AlterColumn (constraint :=> ty) ('Def :=> ty)
689+ -> AlterColumn schema (constraint :=> ty) ('Def :=> ty)
679690setDefault expression = UnsafeAlterColumn $
680691 "SET DEFAULT" <+> renderExpression expression
681692
@@ -690,7 +701,7 @@ setDefault expression = UnsafeAlterColumn $
690701-- in renderDefinition definition
691702-- :}
692703-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP DEFAULT;"
693−dropDefault :: AlterColumn ('Def :=> ty) ('NoDef :=> ty)
704+dropDefault :: AlterColumn schema ('Def :=> ty) ('NoDef :=> ty)
694705dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
695706
696707-- | A `setNotNull` adds a @NOT NULL@ constraint to a column.
@@ -707,7 +718,7 @@ dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
707718-- :}
708719-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET NOT NULL;"
709720setNotNull
710− :: AlterColumn (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
721+ :: AlterColumn schema (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
711722setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
712723
713724-- | A `dropNotNull` drops a @NOT NULL@ constraint from a column.
@@ -722,7 +733,7 @@ setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
722733-- :}
723734-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP NOT NULL;"
724735dropNotNull
725− :: AlterColumn (constraint :=> 'NotNull ty) (constraint :=> 'Null ty)
736+ :: AlterColumn schema (constraint :=> 'NotNull ty) (constraint :=> 'Null ty)
726737dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
727738
728739-- | An `alterType` converts a column to a different data type.
@@ -739,8 +750,8 @@ dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
739750-- in renderDefinition definition
740751-- :}
741752-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" TYPE numeric NOT NULL;"
742−alterType :: TypeExpression ty -> AlterColumn ty0 ty
743−alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderTypeExpression ty
753+alterType :: ColumnTypeExpression schema ty -> AlterColumn schema ty0 ty
754+alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
744755
745756-- | create a view...
746757-- >>> :{
@@ -769,3 +780,60 @@ dropView
769780 => Alias view -- ^ view to remove
770781 -> Definition schema (Drop view schema)
771782dropView v = UnsafeDefinition $ "DROP VIEW" <+> renderAlias v <> ";"
783+
784+createTypeEnum
785+ :: (KnownSymbol enum, SOP.All KnownSymbol labels)
786+ => Alias enum
787+ -> NP Alias labels
788+ -> Definition schema (Create enum ('Typedef ('PGenum labels)) schema)
789+createTypeEnum enum labels = UnsafeDefinition $
790+ "CREATE" <+> "TYPE" <+> renderAlias enum <+> "AS" <+>
791+ parenthesized (commaSeparated (renderAliases labels)) <> ";"
792+
793+-- | `ColumnTypeExpression`s are used in `createTable` commands.
794+newtype ColumnTypeExpression (schema :: SchemaType) (ty :: ColumnType)
795+ = UnsafeColumnTypeExpression { renderColumnTypeExpression :: ByteString }
796+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
797+
798+instance (Has alias schema ('Typedef ty))
799+ => IsLabel alias (ColumnTypeExpression schema ('NoDef :=> 'NotNull ty)) where
800+ fromLabel = UnsafeColumnTypeExpression (renderAlias (fromLabel @alias))
801+
802+null'
803+ :: TypeExpression ty
804+ -> ColumnTypeExpression schema ('NoDef :=> 'Null ty)
805+null' ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NULL"
806+
807+-- | used in `createTable` commands as a column constraint to ensure
808+-- @NULL@ is not present
809+notNull
810+ :: TypeExpression ty
811+ -> ColumnTypeExpression schema (def :=> 'NotNull ty)
812+notNull ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
813+
814+-- | used in `createTable` commands as a column constraint to give a default
815+default_
816+ :: Expression '[] 'Ungrouped '[] ty
817+ -> ColumnTypeExpression schema ('NoDef :=> ty)
818+ -> ColumnTypeExpression schema ('Def :=> ty)
819+default_ x ty = UnsafeColumnTypeExpression $
820+ renderColumnTypeExpression ty <+> "DEFAULT" <+> renderExpression x
821+
822+-- | not a true type, but merely a notational convenience for creating
823+-- unique identifier columns with type `'PGint2`
824+serial2, smallserial
825+ :: ColumnTypeExpression schema ('Def :=> 'NotNull 'PGint2)
826+serial2 = UnsafeColumnTypeExpression "serial2"
827+smallserial = UnsafeColumnTypeExpression "smallserial"
828+-- | not a true type, but merely a notational convenience for creating
829+-- unique identifier columns with type `'PGint4`
830+serial4, serial
831+ :: ColumnTypeExpression schema ('Def :=> 'NotNull 'PGint4)
832+serial4 = UnsafeColumnTypeExpression "serial4"
833+serial = UnsafeColumnTypeExpression "serial"
834+-- | not a true type, but merely a notational convenience for creating
835+-- unique identifier columns with type `'PGint8`
836+serial8, bigserial
837+ :: ColumnTypeExpression schema ('Def :=> 'NotNull 'PGint8)
838+serial8 = UnsafeColumnTypeExpression "serial8"
839+bigserial = UnsafeColumnTypeExpression "bigserial"