@@ -67,6 +67,17 @@ module Squeal.PostgreSQL.Definition
67 -- * Views
67 -- * Views
68 , createView
68 , createView
69 , dropView
69 , dropView
71+ , ColumnTypeExpression (..)
72import Control.Category
83import Control.Category
@@ -111,9 +122,9 @@ CREATE statements
111-- >>> :set -XOverloadedLabels
122-- >>> :set -XOverloadedLabels
113-- renderDefinition $
124-- 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
116−-- "CREATE TABLE \"tab\" (\"a\" int, \"b\" real);"
127+-- "CREATE TABLE \"tab\" (\"a\" int NULL, \"b\" real NULL);"
117createTable
128createTable
118 :: ( KnownSymbol table
129 :: ( KnownSymbol table
119 , columns ~ (col ': cols)
130 , columns ~ (col ': cols)
@@ -121,7 +132,7 @@ createTable
121 , SOP.SListI constraints
132 , SOP.SListI constraints
122 , schema1 ~ Create table ('Table (constraints :=> columns)) schema0 )
133 , schema1 ~ Create table ('Table (constraints :=> columns)) schema0 )
123 => Alias table -- ^ the name of the table to add
134 => Alias table -- ^ the name of the table to add
124− -> NP (Aliased TypeExpression) columns
135+ -> NP (Aliased (ColumnTypeExpression schema0)) columns
125 -- ^ the names and datatype of each column
136 -- ^ the names and datatype of each column
126 -> NP (Aliased (TableConstraintExpression schema1 table)) constraints
137 -> NP (Aliased (TableConstraintExpression schema1 table)) constraints
127 -- ^ constraints that must hold for the table
138 -- ^ constraints that must hold for the table
@@ -138,15 +149,15 @@ createTable tab columns constraints = UnsafeDefinition $
138-- >>> type Schema = '["tab" ::: 'Table Table]
149-- >>> type Schema = '["tab" ::: 'Table Table]
140-- renderDefinition
151-- 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)
143−-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int, \"b\" real);"
154+-- "CREATE TABLE IF NOT EXISTS \"tab\" (\"a\" int NULL, \"b\" real NULL);"
144createTableIfNotExists
155createTableIfNotExists
145 :: ( Has table schema ('Table (constraints :=> columns))
156 :: ( Has table schema ('Table (constraints :=> columns))
146 , SOP.SListI columns
157 , SOP.SListI columns
147 , SOP.SListI constraints )
158 , SOP.SListI constraints )
148 => Alias table -- ^ the name of the table to add
159 => Alias table -- ^ the name of the table to add
149− -> NP (Aliased TypeExpression) columns
160+ -> NP (Aliased (ColumnTypeExpression schema)) columns
150 -- ^ the names and datatype of each column
161 -- ^ the names and datatype of each column
151 -> NP (Aliased (TableConstraintExpression schema table)) constraints
162 -> NP (Aliased (TableConstraintExpression schema table)) constraints
152 -- ^ constraints that must hold for the table
163 -- ^ constraints that must hold for the table
@@ -161,9 +172,9 @@ renderCreation
161 , SOP.SListI columns
172 , SOP.SListI columns
162 , SOP.SListI constraints )
173 , SOP.SListI constraints )
163 => Alias table -- ^ the name of the table to add
174 => Alias table -- ^ the name of the table to add
164− -> NP (Aliased TypeExpression) columns
175+ -> NP (Aliased (ColumnTypeExpression schema0)) columns
165 -- ^ the names and datatype of each column
176 -- ^ the names and datatype of each column
166− -> NP (Aliased (TableConstraintExpression schema table)) constraints
177+ -> NP (Aliased (TableConstraintExpression schema1 table)) constraints
167 -- ^ constraints that must hold for the table
178 -- ^ constraints that must hold for the table
168 -> ByteString
179 -> ByteString
169renderCreation tab columns constraints = renderAlias tab
180renderCreation tab columns constraints = renderAlias tab
@@ -175,9 +186,9 @@ renderCreation tab columns constraints = renderAlias tab
175 renderCommaSeparated renderConstraint constraints ) )
186 renderCommaSeparated renderConstraint constraints ) )
178− renderColumnDef :: Aliased TypeExpression x -> ByteString
189+ renderColumnDef :: Aliased (ColumnTypeExpression schema) x -> ByteString
179 renderColumnDef (ty `As` column) =
190 renderColumnDef (ty `As` column) =
180− renderAlias column <+> renderTypeExpression ty
191+ renderAlias column <+> renderColumnTypeExpression ty
181 renderConstraint
192 renderConstraint
182 :: Aliased (TableConstraintExpression schema columns) constraint
193 :: Aliased (TableConstraintExpression schema columns) constraint
183 -> ByteString
194 -> ByteString
@@ -251,13 +262,13 @@ check _cols condition = UnsafeTableConstraintExpression $
252-- definition :: Definition '[] Schema
263-- definition :: Definition '[] Schema
253-- definition = createTable #tab
264-- definition = createTable #tab
254−-- ( int `As` #a :*
265+-- ( (int & null') `As` #a :*
255−-- int `As` #b :* Nil )
266+-- (int & null') `As` #b :* Nil )
256-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
267-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
259-- >>> renderDefinition definition
270-- >>> 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\"));"
262 :: ( Has alias schema ('Table table)
273 :: ( Has alias schema ('Table table)
263 , HasAll aliases (TableToColumns table) subcolumns )
274 , HasAll aliases (TableToColumns table) subcolumns )
@@ -332,13 +343,13 @@ primaryKey columns = UnsafeTableConstraintExpression $
332-- createTable #emails
343-- createTable #emails
333-- ( serial `As` #id :*
344-- ( serial `As` #id :*
334-- (int & notNull) `As` #user_id :*
345-- (int & notNull) `As` #user_id :*
335−-- text `As` #email :* Nil )
346+-- (text & null') `As` #email :* Nil )
336-- ( primaryKey #id `As` #pk_emails :*
347-- ( primaryKey #id `As` #pk_emails :*
337-- foreignKey #user_id #users #id
348-- foreignKey #user_id #users #id
338-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
349-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
339-- in renderDefinition setup
350-- in renderDefinition setup
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);"
343-- A `foreignKey` can even be a table self-reference.
354-- A `foreignKey` can even be a table self-reference.
@@ -362,13 +373,13 @@ primaryKey columns = UnsafeTableConstraintExpression $
362-- createTable #employees
373-- createTable #employees
363-- ( serial `As` #id :*
374-- ( serial `As` #id :*
364-- (text & notNull) `As` #name :*
375-- (text & notNull) `As` #name :*
365−-- integer `As` #employer_id :* Nil )
376+-- (integer & null') `As` #employer_id :* Nil )
366-- ( primaryKey #id `As` #employees_pk :*
377-- ( primaryKey #id `As` #employees_pk :*
367-- foreignKey #employer_id #employees #id
378-- foreignKey #employer_id #employees #id
368-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
379-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
369-- in renderDefinition setup
380-- in renderDefinition setup
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);"
373foreignKey
384foreignKey
374 :: (ForeignKeyed schema child parent
385 :: (ForeignKeyed schema child parent
@@ -564,10 +575,10 @@ class AddColumn ty where
564 -- '["tab" ::: 'Table ('[] :=>
575 -- '["tab" ::: 'Table ('[] :=>
565 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
576 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
566 -- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
577 -- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
567− -- definition = alterTable #tab (addColumn #col2 (text & default_ "foo"))
578+ -- definition = alterTable #tab (addColumn #col2 (text & null' & default_ "foo"))
568 -- in renderDefinition definition
579 -- in renderDefinition definition
570− -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text DEFAULT E'foo';"
581+ -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL DEFAULT E'foo';"
572 -- >>> :{
583 -- >>> :{
@@ -576,20 +587,20 @@ class AddColumn ty where
576 -- '["tab" ::: 'Table ('[] :=>
587 -- '["tab" ::: 'Table ('[] :=>
577 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
588 -- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
578 -- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
589 -- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
579− -- definition = alterTable #tab (addColumn #col2 text)
590+ -- definition = alterTable #tab (addColumn #col2 (text & null'))
580 -- in renderDefinition definition
591 -- in renderDefinition definition
582− -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text;"
593+ -- "ALTER TABLE \"tab\" ADD COLUMN \"col2\" text NULL;"
583 addColumn
594 addColumn
584 :: ( KnownSymbol column
595 :: ( KnownSymbol column
585 , Has tab schema ('Table table0)
596 , Has tab schema ('Table table0)
586 , table0 ~ (constraints :=> columns)
597 , table0 ~ (constraints :=> columns)
587 , table1 ~ (constraints :=> Create column ty columns) )
598 , table1 ~ (constraints :=> Create column ty columns) )
588 => Alias column -- ^ column to add
599 => Alias column -- ^ column to add
589− -> TypeExpression ty -- ^ type of the new column
600+ -> ColumnTypeExpression schema ty -- ^ type of the new column
590 -> AlterTable tab table1 schema
601 -> AlterTable tab table1 schema
591 addColumn column ty = UnsafeAlterTable $
602 addColumn column ty = UnsafeAlterTable $
592− "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
603+ "ADD COLUMN" <+> renderAlias column <+> renderColumnTypeExpression ty
593instance {-# OVERLAPPING #-} AddColumn ('Def :=> ty)
604instance {-# OVERLAPPING #-} AddColumn ('Def :=> ty)
594instance {-# OVERLAPPABLE #-} AddColumn ('NoDef :=> 'Null ty)
605instance {-# OVERLAPPABLE #-} AddColumn ('NoDef :=> 'Null ty)
@@ -650,13 +661,13 @@ alterColumn
650 , Has column columns ty0
661 , Has column columns ty0
651 , tables1 ~ (constraints :=> Alter column ty1 columns))
662 , tables1 ~ (constraints :=> Alter column ty1 columns))
652 => Alias column -- ^ column to alter
663 => Alias column -- ^ column to alter
653− -> AlterColumn ty0 ty1 -- ^ alteration to perform
664+ -> AlterColumn schema ty0 ty1 -- ^ alteration to perform
654 -> AlterTable tab table1 schema
665 -> AlterTable tab table1 schema
655alterColumn column alteration = UnsafeAlterTable $
666alterColumn column alteration = UnsafeAlterTable $
656 "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
667 "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
658-- | An `AlterColumn` describes the alteration to perform on a single column.
669-- | 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) =
660 UnsafeAlterColumn {renderAlterColumn :: ByteString}
671 UnsafeAlterColumn {renderAlterColumn :: ByteString}
661 deriving (GHC.Generic,Show,Eq,Ord,NFData)
672 deriving (GHC.Generic,Show,Eq,Ord,NFData)
@@ -675,7 +686,7 @@ newtype AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
675-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET DEFAULT 5;"
686-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET DEFAULT 5;"
676setDefault
687setDefault
677 :: Expression '[] 'Ungrouped '[] ty -- ^ default value to set
688 :: Expression '[] 'Ungrouped '[] ty -- ^ default value to set
678− -> AlterColumn (constraint :=> ty) ('Def :=> ty)
689+ -> AlterColumn schema (constraint :=> ty) ('Def :=> ty)
679setDefault expression = UnsafeAlterColumn $
690setDefault expression = UnsafeAlterColumn $
680 "SET DEFAULT" <+> renderExpression expression
691 "SET DEFAULT" <+> renderExpression expression
@@ -690,7 +701,7 @@ setDefault expression = UnsafeAlterColumn $
690-- in renderDefinition definition
701-- in renderDefinition definition
692-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP DEFAULT;"
703-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP DEFAULT;"
693−dropDefault :: AlterColumn ('Def :=> ty) ('NoDef :=> ty)
704+dropDefault :: AlterColumn schema ('Def :=> ty) ('NoDef :=> ty)
694dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
705dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
696-- | A `setNotNull` adds a @NOT NULL@ constraint to a column.
707-- | A `setNotNull` adds a @NOT NULL@ constraint to a column.
@@ -707,7 +718,7 @@ dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
708-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET NOT NULL;"
719-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" SET NOT NULL;"
709setNotNull
720setNotNull
710− :: AlterColumn (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
721+ :: AlterColumn schema (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
711setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
722setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
713-- | A `dropNotNull` drops a @NOT NULL@ constraint from a column.
724-- | A `dropNotNull` drops a @NOT NULL@ constraint from a column.
@@ -722,7 +733,7 @@ setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
723-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP NOT NULL;"
734-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" DROP NOT NULL;"
724dropNotNull
735dropNotNull
725− :: AlterColumn (constraint :=> 'NotNull ty) (constraint :=> 'Null ty)
736+ :: AlterColumn schema (constraint :=> 'NotNull ty) (constraint :=> 'Null ty)
726dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
737dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
728-- | An `alterType` converts a column to a different data type.
739-- | An `alterType` converts a column to a different data type.
@@ -739,8 +750,8 @@ dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
739-- in renderDefinition definition
750-- in renderDefinition definition
741-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" TYPE numeric NOT NULL;"
752-- "ALTER TABLE \"tab\" ALTER COLUMN \"col\" TYPE numeric NOT NULL;"
742−alterType :: TypeExpression ty -> AlterColumn ty0 ty
753+alterType :: ColumnTypeExpression schema ty -> AlterColumn schema ty0 ty
743−alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderTypeExpression ty
754+alterType ty = UnsafeAlterColumn $ "TYPE" <+> renderColumnTypeExpression ty
745-- | create a view...
756-- | create a view...
@@ -769,3 +780,60 @@ dropView
769 => Alias view -- ^ view to remove
780 => Alias view -- ^ view to remove
770 -> Definition schema (Drop view schema)
781 -> Definition schema (Drop view schema)
771dropView v = UnsafeDefinition $ "DROP VIEW" <+> renderAlias v <> ";"
782dropView v = UnsafeDefinition $ "DROP VIEW" <+> renderAlias v <> ";"
785+ :: (KnownSymbol enum, SOP.All KnownSymbol 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)) <> ";"
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)
798+instance (Has alias schema ('Typedef ty))
799+ => IsLabel alias (ColumnTypeExpression schema ('NoDef :=> 'NotNull ty)) where
800+ fromLabel = UnsafeColumnTypeExpression (renderAlias (fromLabel @alias))
803+ :: TypeExpression ty
804+ -> ColumnTypeExpression schema ('NoDef :=> 'Null ty)
805+null' ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NULL"
807+-- | used in `createTable` commands as a column constraint to ensure
808+-- @NULL@ is not present
810+ :: TypeExpression ty
811+ -> ColumnTypeExpression schema (def :=> 'NotNull ty)
812+notNull ty = UnsafeColumnTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
814+-- | used in `createTable` commands as a column constraint to give a 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
822+-- | not a true type, but merely a notational convenience for creating
823+-- unique identifier columns with type `'PGint2`
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`
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`
837+ :: ColumnTypeExpression schema ('Def :=> 'NotNull 'PGint8)
838+serial8 = UnsafeColumnTypeExpression "serial8"
839+bigserial = UnsafeColumnTypeExpression "bigserial"