on dev help sign in
foreign key fixes
39d641bEitan Chatav
squeal-postgresql/src/Squeal/PostgreSQL/Definition.hs+30−24
@@ -9,7 +9,8 @@ Squeal data definition language.
9-}
9-}
10
10
11{-# LANGUAGE
11{-# LANGUAGE
12+ ConstraintKinds
12− DataKinds
13+ , DataKinds
13 , DeriveDataTypeable
14 , DeriveDataTypeable
14 , DeriveGeneric
15 , DeriveGeneric
15 , FlexibleContexts
16 , FlexibleContexts
@@ -41,6 +42,7 @@ module Squeal.PostgreSQL.Definition
41 , unique
42 , unique
42 , primaryKey
43 , primaryKey
43 , foreignKey
44 , foreignKey
45+ , ForeignKeyed
44 , OnDeleteClause (OnDeleteNoAction, OnDeleteRestrict, OnDeleteCascade)
46 , OnDeleteClause (OnDeleteNoAction, OnDeleteRestrict, OnDeleteCascade)
45 , renderOnDeleteClause
47 , renderOnDeleteClause
46 , OnUpdateClause (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
48 , OnUpdateClause (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
@@ -124,7 +126,9 @@ createTable
124createTable table columns constraints = UnsafeDefinition $
126createTable table columns constraints = UnsafeDefinition $
125 "CREATE TABLE" <+> renderCreation table columns constraints
127 "CREATE TABLE" <+> renderCreation table columns constraints
126
128
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`.
128--
132--
129-- >>> :set -XOverloadedLabels -XTypeApplications
133-- >>> :set -XOverloadedLabels -XTypeApplications
130-- >>> type Table = '[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGfloat4]
134-- >>> type Table = '[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGfloat4]
@@ -277,12 +281,12 @@ primaryKey columns = UnsafeTableConstraintExpression $
277-- >>> :{
281-- >>> :{
278-- type Schema =
282-- type Schema =
279-- '[ "users" :::
283-- '[ "users" :::
280−-- '[ "pk_id" ::: 'PrimaryKey '["id"] ] :=>
284+-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
281-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
285-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
282-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
286-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
283-- ]
287-- ]
284-- , "emails" :::
288-- , "emails" :::
285−-- '[ "pk_id" ::: 'PrimaryKey '["id"]
289+-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
286-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
290-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
287-- ] :=>
291-- ] :=>
288-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
292-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
@@ -292,36 +296,31 @@ primaryKey columns = UnsafeTableConstraintExpression $
292-- ]
296-- ]
293-- :}
297-- :}
294--
298--
295−-- :{
299+-- >>> :{
296-- let
300-- let
297-- setup :: Definition '[] Schema
301-- setup :: Definition '[] Schema
298-- setup =
302-- setup =
299−-- createTable #users
303+-- createTable #users
300−-- ( #id serial :*
304+-- ( serial `As` #id :*
301−-- #name (text & notNull) :* Nil )
305+-- (text & notNull) `As` #name :* Nil )
302−-- ( #pk_id (primaryKey (Column #id :* Nil)) :* Nil ) >>>
306+-- ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil ) >>>
303−-- createTable #emails
307+-- createTable #emails
304−-- ( #id serial :*
308+-- ( serial `As` #id :*
305−-- #user_id (int & notNull) :*
309+-- (int & notNull) `As` #user_id :*
306−-- #email text :* Nil )
310+-- text `As` #email :* Nil )
307−-- ( #pk_id (primaryKey (Column #id :* Nil)) :*
311+-- ( primaryKey (Column #id :* Nil) `As` #pk_emails :*
308−-- #fk_user_id (foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
312+-- foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
309−-- OnDeleteCascade OnUpdateCascade) :* Nil )
313+-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
310-- in renderDefinition setup
314-- in renderDefinition setup
311-- :}
315-- :}
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);"
313foreignKey
317foreignKey
314− :: ( Has table schema reftable
318+ :: ForeignKeyed schema table reftable subcolumns refsubcolumns
315− , refcolumns ~ TableToColumns reftable
316− , SameTypes subcolumns refsubcolumns
317− , AllNotNull subcolumns
318− , SOP.SListI subcolumns
319− , SOP.SListI refsubcolumns)
320 => NP (Column columns) subcolumns
319 => NP (Column columns) subcolumns
321 -- ^ column or columns in the table
320 -- ^ column or columns in the table
322 -> Alias table
321 -> Alias table
323 -- ^ reference table
322 -- ^ reference table
324− -> NP (Column refcolumns) refsubcolumns
323+ -> NP (Column (TableToColumns reftable)) refsubcolumns
325 -- ^ reference column or columns in the reference table
324 -- ^ reference column or columns in the reference table
326 -> OnDeleteClause
325 -> OnDeleteClause
327 -- ^ what to do when reference is deleted
326 -- ^ what to do when reference is deleted
@@ -337,6 +336,13 @@ foreignKey columns reftable refcolumns onDelete onUpdate =
337 <+> renderOnDeleteClause onDelete
336 <+> renderOnDeleteClause onDelete
338 <+> renderOnUpdateClause onUpdate
337 <+> renderOnUpdateClause onUpdate
339
338
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+
340-- | `OnDelete` indicates what to do with rows that reference a deleted row.
346-- | `OnDelete` indicates what to do with rows that reference a deleted row.
341data OnDeleteClause
347data OnDeleteClause
342 = OnDeleteNoAction
348 = OnDeleteNoAction