@@ -9,7 +9,8 @@ Squeal data definition language.
99-}
1010
1111{-# LANGUAGE
12− DataKinds
12+ ConstraintKinds
13+ , DataKinds
1314 , DeriveDataTypeable
1415 , DeriveGeneric
1516 , FlexibleContexts
@@ -41,6 +42,7 @@ module Squeal.PostgreSQL.Definition
4142 , unique
4243 , primaryKey
4344 , foreignKey
45+ , ForeignKeyed
4446 , OnDeleteClause (OnDeleteNoAction, OnDeleteRestrict, OnDeleteCascade)
4547 , renderOnDeleteClause
4648 , OnUpdateClause (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
@@ -124,7 +126,9 @@ createTable
124126createTable table columns constraints = UnsafeDefinition $
125127 "CREATE TABLE" <+> renderCreation table columns constraints
126128
127−-- | `createTable` adds a table to the schema.
129+-- | `createTableIfNotExists` creates a table if it doesn't exist, but does not add it to the schema.
130+-- Instead, the schema already has the table so if the table did not yet exist, the schema was wrong.
131+-- `createTableIfNotExists` fixes this. Interestingly, this property makes it an idempotent in the `Category` `Definition`.
128132--
129133-- >>> :set -XOverloadedLabels -XTypeApplications
130134-- >>> type Table = '[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGfloat4]
@@ -277,12 +281,12 @@ primaryKey columns = UnsafeTableConstraintExpression $
277281-- >>> :{
278282-- type Schema =
279283-- '[ "users" :::
280−-- '[ "pk_id" ::: 'PrimaryKey '["id"] ] :=>
284+-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
281285-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
282286-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
283287-- ]
284288-- , "emails" :::
285−-- '[ "pk_id" ::: 'PrimaryKey '["id"]
289+-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
286290-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
287291-- ] :=>
288292-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
@@ -292,36 +296,31 @@ primaryKey columns = UnsafeTableConstraintExpression $
292296-- ]
293297-- :}
294298--
295−-- :{
299+-- >>> :{
296300-- let
297301-- setup :: Definition '[] Schema
298302-- setup =
299−-- createTable #users
300−-- ( #id serial :*
301−-- #name (text & notNull) :* Nil )
302−-- ( #pk_id (primaryKey (Column #id :* Nil)) :* Nil ) >>>
303−-- createTable #emails
304−-- ( #id serial :*
305−-- #user_id (int & notNull) :*
306−-- #email text :* Nil )
307−-- ( #pk_id (primaryKey (Column #id :* Nil)) :*
308−-- #fk_user_id (foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
309−-- OnDeleteCascade OnUpdateCascade) :* Nil )
303+-- createTable #users
304+-- ( serial `As` #id :*
305+-- (text & notNull) `As` #name :* Nil )
306+-- ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil ) >>>
307+-- createTable #emails
308+-- ( serial `As` #id :*
309+-- (int & notNull) `As` #user_id :*
310+-- text `As` #email :* Nil )
311+-- ( primaryKey (Column #id :* Nil) `As` #pk_emails :*
312+-- foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
313+-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
310314-- in renderDefinition setup
311315-- :}
312−-- "CREATE TABLE users (id serial, username text NOT NULL, CONSTRAINT pk_id PRIMARY KEY (id)); CREATE TABLE emails (id serial, userid integer NOT NULL, email text NOT NULL, CONSTRAINT pk_id PRIMARY KEY (id), CONSTRAINT fk_user_id FOREIGN KEY (userid) REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT);"
316+-- "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);"
313317foreignKey
314− :: ( Has table schema reftable
315− , refcolumns ~ TableToColumns reftable
316− , SameTypes subcolumns refsubcolumns
317− , AllNotNull subcolumns
318− , SOP.SListI subcolumns
319− , SOP.SListI refsubcolumns)
318+ :: ForeignKeyed schema table reftable subcolumns refsubcolumns
320319 => NP (Column columns) subcolumns
321320 -- ^ column or columns in the table
322321 -> Alias table
323322 -- ^ reference table
324− -> NP (Column refcolumns) refsubcolumns
323+ -> NP (Column (TableToColumns reftable)) refsubcolumns
325324 -- ^ reference column or columns in the reference table
326325 -> OnDeleteClause
327326 -- ^ what to do when reference is deleted
@@ -337,6 +336,13 @@ foreignKey columns reftable refcolumns onDelete onUpdate =
337336 <+> renderOnDeleteClause onDelete
338337 <+> renderOnUpdateClause onUpdate
339338
339+type ForeignKeyed schema table reftable subcolumns refsubcolumns
340+ = ( Has table schema reftable
341+ , SameTypes subcolumns refsubcolumns
342+ , AllNotNull subcolumns
343+ , SOP.SListI subcolumns
344+ , SOP.SListI refsubcolumns )
345+
340346-- | `OnDelete` indicates what to do with rows that reference a deleted row.
341347data OnDeleteClause
342348 = OnDeleteNoAction