on dev-params help sign in
squeal/RELEASE NOTES.md
817 lines 25 kB history raw
1## RELEASE NOTES
3### Version 0.4
5Version 0.4 strengthens Squeal's type system, adds
6support for multidimensional arrays, improves support
7for container type, improves `with` statements,
8improves runtime exceptions, accomodates SQL's three-valued logic,
9adds subquery expressions, and adds table and view type expressions.
11**Types**
13Squeal 0.4 renames some kinds to aid intuition:
15```Haskell
16type RowType = [(Symbol, NullityType)] -- previously RelationType
17type FromType = [(Symbol, RowType)] -- previously RelationsType
18```
20Null safety for array and composite types is gained by having the base
21type of an array be a `NullityType` and the base type of a composite
22a `RowType`.
24```Haskell
25data PGType
26 = ..
27 | PGvararray NullityType
28 | PGfixarray Nat NullityType
29 | PGcomposite RowType
30```
32Squeal embeds Postgres types into Haskell using data kinds and type-in-type:
34```Haskell
35data PGType = PGbool | ..
36data NullityType = Null PGType | NotNull PGType
37type RowType = [(Symbol, NullityType)] -- previously RelationType
38type FromType = [(Symbol, RowType)] -- previously RelationsType
39```
41In another sense, we can embed Haskell types
42into Postgres types by providing type families:
44```Haskell
45type family PG (hask :: Type) :: PGType
46type family NullPG (hask :: Type) :: NullityType
47type family TuplePG (hask :: Type) :: [NullityType]
48type family RowPG (hask :: Type) :: RowType
49```
51Let's look at these one by one.
53`PG` was introduced in Squeal 0.3. It was a closed type family that
54associates some Haskell types to their obvious corresponding Postgres
55types like `PG Double = 'PGfloat8`. It only worked on base types,
56no arrays or composites. Squeal 0.4 extends it to
57such container types and makes it an open type family so that
58users can make their own type instances.
60`NullPG` had a different name before but it does the obvious
61thing for Haskell with `Maybe`s:
63```Haskell
64type family NullPG hask where
65 NullPG (Maybe hask) = 'Null (PG hask)
66 NullPG hask = 'NotNull (PG hask)
67```
69`TuplePG` uses generics to turn tuple types (including records)
70into lists of `NullityType`s in the logical way, e.g.
71`TuplePG (Bool, Day) = '[ 'PGbool, 'PGdate]`.
73`RowPG` also uses generics to turn record types into a `RowType` in the logical way, e.g.
75```Haskell
76>>> data Person = Person { name :: Text, age :: Int32 } deriving GHC.Generic
77>>> instance SOP.Generic Person
78>>> instance SOP.HasDatatypeInfo Person
79>>> :kind! TuplePG Person
80TuplePG Person :: [NullityType]
81= '['NotNull 'PGtext, 'NotNull 'PGint4]
82>>> :kind! RowPG Person
83RowPG Person :: [(Symbol, NullityType)]
84= '["name" ::: 'NotNull 'PGtext, "age" ::: 'NotNull 'PGint4]
85```
87We've already seen a hint of why these types are useful in one construction
88from Squeal 0.3. Creating composite types in Postgres directly from a Haskell
89record type essentially uses `RowPG`. Another important use is in simplifying
90the type signatures for a `Query` or `Manipulation`. Very often, you will have
91a tuple type corresponding to the parameters and a record type corresponding
92to the returned columns of a `Query` or `Manipulation`. Instead of writing
93boilerplate signature you can reuse these with the help of `TuplePG` and `RowPG`
95For instance:
97```Haskell
98>>> :{
99let
100 query :: Query '["user" ::: 'View (RowPG Person)] (TuplePG (Only Int32)) (RowPG Person)
101 query = selectStar (from (view #user) & where_ (#age .> param @1))
102:}
103```
105**Arrays**
107In addition to being able to encode and decode basic Haskell types
108like `Int16` and `Text`, Squeal 0.4 permits you to encode and decode Haskell types to
109Postgres array types. The `Vector` type corresponds to to variable length arrays.
110And thanks to an idea from [Mike Ledger](https://github.com/mikeplus64),
111homogeneous tuples correspond to fixed length arrays. We can even
112create multi-dimensional fixed length arrays. Let's see an example.
114```Haskell
115>>> :{
116data Row = Row
117 { col1 :: Vector Int16
118 , col2 :: (Maybe Int16,Maybe Int16)
119 , col3 :: ((Int16,Int16),(Int16,Int16),(Int16,Int16))
120 } deriving (Eq, GHC.Generic)
121:}
123>>> instance Generic Row
124>>> instance HasDatatypeInfo Row
125```
127Define a simple round trip query.
129```Haskell
130>>> :{
131let
132 roundTrip :: Query '[] (TuplePG Row) (RowPG Row)
133 roundTrip = values_ $
134 parameter @1 (int2 & vararray) `as` #col1 :*
135 parameter @2 (int2 & fixarray @2) `as` #col2 :*
136 parameter @3 (int2 & fixarray @2 & fixarray @3) `as` #col3
137:}
139>>> :set -XOverloadedLists
140>>> let input = Row [1,2] (Just 1,Nothing) ((1,2),(3,4),(5,6))
141>>> :{
142void . withConnection "host=localhost port=5432 dbname=exampledb" $ do
143 result <- runQueryParams roundTrip input
144 Just output <- firstRow result
145 liftBase . print $ input == output
146:}
147True
148```
150**Containers**
152Squeal aims to provide a correspondence between Haskell types and Postgres types.
153In particular, Haskell ADTs with nullary constructors can correspond to
154Postgres enum types and Haskell record types can correspond to Postgres
155composite types. However, it's not always obvious that that's how a user
156will choose to store values of those types. So Squeal 0.4 introduces newtypes
157whose purpose is to specify how a user wants to store values of a type.
159```Haskell
160newtype Json hask = Json {getJson :: hask}
161newtype Jsonb hask = Jsonb {getJsonb :: hask}
162newtype Composite record = Composite {getComposite :: record}
163newtype Enumerated enum = Enumerated {getEnumerated :: enum}
164```
166Let's see an example:
168```Haskell
169>>> data Schwarma = Beef | Lamb | Chicken deriving (Eq, Show, GHC.Generic)
170>>> instance SOP.Generic Schwarma
171>>> instance SOP.HasDatatypeInfo Schwarma
172>>>
173>>> data Person = Person {name :: Text, age :: Int32} deriving (Eq, Show, GHC.Generic)
174>>> instance SOP.Generic Person
175>>> instance SOP.HasDatatypeInfo Person
176>>> instance Aeson.FromJSON Person
177>>> instance Aeson.ToJSON Person
178```
180We can create the equivalent Postgres types directly from their Haskell types.
182```Haskell
183>>> :{
184type Schema =
185 '[ "schwarma" ::: 'Typedef (PG (Enumerated Schwarma))
186 , "person" ::: 'Typedef (PG (Composite Person))
187 ]
188:}
190>>> :{
191let
192 setup :: Definition '[] Schema
193 setup =
194 createTypeEnumFrom @Schwarma #schwarma >>>
195 createTypeCompositeFrom @Person #person
196:}
197```
199Let's demonstrate how to associate our Haskell types `Schwarma` and `Person`
200with enumerated, composite or json types in Postgres. First create a Haskell
201`Row` type using the `Enumerated`, `Composite` and `Json` newtypes as fields.
203```Haskell
204>>> :{
205data Row = Row
206 { schwarma :: Enumerated Schwarma
207 , person1 :: Composite Person
208 , person2 :: Json Person
209 } deriving (Eq, GHC.Generic)
210:}
212>>> instance Generic Row
213>>> instance HasDatatypeInfo Row
214>>> :{
215let
216 input = Row
217 (Enumerated Chicken)
218 (Composite (Person "Faisal" 24))
219 (Json (Person "Ahmad" 48))
220:}
221```
223Once again, define a round trip query.
225```Haskell
226>>> :{
227let
228 roundTrip :: Query Schema (TuplePG Row) (RowPG Row)
229 roundTrip = values_ $
230 parameter @1 (typedef #schwarma) `as` #schwarma :*
231 parameter @2 (typedef #person) `as` #person1 :*
232 parameter @3 json `as` #person2
233:}
234```
236Finally, we can drop our type definitions.
238```Haskell
239>>> :{
240let
241 teardown :: Definition Schema '[]
242 teardown = dropType #schwarma >>> dropType #person
243:}
244```
246Now let's run it.
248```Haskell
249>>> :{
250let
251 session = do
252 result <- runQueryParams roundTrip input
253 Just output <- firstRow result
254 liftBase . print $ input == output
255in
256 void . withConnection "host=localhost port=5432 dbname=exampledb" $
257 define setup
258 & pqThen session
259 & pqThen (define teardown)
260:}
261True
262```
264**With**
266Squeal 0.3 supported WITH statements but there's a couple problems with them.
267Here's the type signature for `with` in Squeal 0.3.
269```Haskell
270with
271 :: SOP.SListI commons
272 => NP (Aliased (Manipulation schema params)) (common ': commons)
273 -- ^ common table expressions
274 -> Manipulation (With (common ': commons) schema) params results
275 -> Manipulation schema params results
276```
278The first problem is that `with` only works with `Manipulations`.
279It can work on `Query`s by using `queryStatement` but it still will
280return a `Manipulation`. We can fix this issue by making `with` a
281method of a type class with instances for both `Query` and `Manipulation`.
283The second problem is that all the common table expressions
284refer the base schema, whereas in SQL, each subsequent CTE can
285refer to previous CTEs as well. But this can be fixed! First define a datatype:
287```Haskell
288data CommonTableExpression statement params schema0 schema1 where
289 CommonTableExpression
290 :: Aliased (statement schema params) (alias ::: cte)
291 -> CommonTableExpression statement params schema (alias ::: 'View cte ': schema)
292```
294It's just a wrapper around an aliased statement, where the statement
295could be either a `Query` or `Manipulation`, but it augments the schema
296by adding a view to it. It almost looks like a morphism between schemas
297but there is no way yet to compose them. Luckily, Squeal already has
298a datatype for this, which is used for migrations, the `AlignedList`
299type which is really the "free category". So we can then define a `With` type class:
301```Haskell
302class With statement where
303 with
304 :: AlignedList (CommonTableExpression statement params) schema0 schema1
305 -> statement schema1 params row
306 -> statement schema0 params row
307```
309By giving `Aliasable` instances to CTEs and aligned singleton lists of CTEs (i.e. scrap-your-nils), we get a nice syntax for WITH statements in Squeal.
311Here's an example of using `with` for a `Query` and a `Manipulation`:
313```Haskell
314>>> :{
315let
316 query :: Query
317 '[ "t1" ::: 'View
318 '[ "c1" ::: 'NotNull 'PGtext
319 , "c2" ::: 'NotNull 'PGtext] ]
320 '[]
321 '[ "c1" ::: 'NotNull 'PGtext
322 , "c2" ::: 'NotNull 'PGtext ]
323 query = with (
324 selectStar (from (view #t1)) `as` #t2 :>>
325 selectStar (from (view #t2)) `as` #t3
326 ) (selectStar (from (view #t3)))
327in printSQL query
328:}
329WITH "t2" AS (SELECT * FROM "t1" AS "t1"), "t3" AS (SELECT * FROM "t2" AS "t2") SELECT * FROM "t3" AS "t3"
331>>> type ProductsTable = '[] :=> '["product" ::: 'NoDef :=> 'NotNull 'PGtext, "date" ::: 'Def :=> 'NotNull 'PGdate]
333>>> :{
334let
335 manipulation :: Manipulation
336 '[ "products" ::: 'Table ProductsTable
337 , "products_deleted" ::: 'Table ProductsTable
338 ] '[ 'NotNull 'PGdate] '[]
339 manipulation = with
340 (deleteFrom #products (#date .< param @1) ReturningStar `as` #deleted_rows)
341 (insertQuery_ #products_deleted (selectStar (from (view (#deleted_rows `as` #t)))))
342in printSQL manipulation
343:}
344WITH "deleted_rows" AS (DELETE FROM "products" WHERE ("date" < ($1 :: date)) RETURNING *) INSERT INTO "products_deleted" SELECT * FROM "deleted_rows" AS "t"
345```
347**Three Valued Logic**
349In previous versions of Squeal, conditions followed classical two valued logic
350of `true` and `false`.
352```Haskell
353-- Squeal 0.3
354type Condition schema from grouping params =
355 Expression schema from grouping params ('NotNull 'PGbool)
356```
358I had thought that three valued logic, which is what SQL uses, was confusing.
359However, multiple users reported being confused at being forced to do `NULL`
360handling, particularly in their left joins. Since the original motivation
361of being less confusing evaporated I decided to switch to three valued logic
362of `true`, `false` and `null_`.
364```Haskell
365-- Squeal 0.4
366type Condition schema from grouping params =
367 Expression schema from grouping params ('Null 'PGbool)
368```
370**Subquery Expressions**
372Squeal 0.4 adds support for subquery expressions such as `IN` and `op ANY/ALL`.
374**Row Types**
376Squeal 0.4 adds functions to define type expressions from tables and views
377and a type expression for user defined types, `typetable`, `typeview` and
378`typedef`.
380**Runtime Exceptions**
382Squeal now has an exception type which gives details on the sort of error
383encountered and handler functions.
385```Haskell
386data SquealException
387 = PQException
388 { sqlExecStatus :: LibPQ.ExecStatus
389 , sqlStateCode :: Maybe ByteString
390 -- ^ https://www.postgresql.org/docs/current/static/errcodes-appendix.html
391 , sqlErrorMessage :: Maybe ByteString
392 }
393 | ResultException Text
394 | ParseException Text
395 deriving (Eq, Show)
396instance Exception SquealException
398catchSqueal
399 :: MonadBaseControl IO io
400 => io a
401 -> (SquealException -> io a) -- ^ handler
402 -> io a
404handleSqueal
405 :: MonadBaseControl IO io
406 => (SquealException -> io a) -- ^ handler
407 -> io a -> io a
408```
410**Additional Changes**
412Squeal 0.4 adds `field` and `index` functions to get components of composite
413and array expressions.
415Squeal 0.4 adds a dependency on `records-sop` to offload a lot of boilerplate
416type family logic that was needed for `RowPG`.
418The above changes required major and minor changes to Squeal DSL functions.
419Please consult the documentation.
421### Version 0.3.2 - August 4, 2018
423Version 0.3.2 features extensive support for `JSON` functionality with
424more than 50 new functions.
425This work is entirely due to [Mike Ledger](https://github.com/mikeplus64)
426who has been making terrific contributions to Squeal. Thanks!
427We also got some examples in the documentation for pools submitted by
428[Raveline](https://github.com/Raveline). I'm so pleased to be
429getting pull requests and issue submissions from you all!
431### Version 0.3.1 - July 7, 2018
433Version 0.3.1 of Squeal enables the "Scrap your Nils" trick for
434heterogeneous lists of `Alias`es, `Aliased` expressions, `PGlabel`s and `By`s
435with the typeclasses `IsLabel`, `IsQualified`, `IsPGlabel`,
436and the new `Aliasable` typeclass, to eliminate all need of using `Nil` in a list.
437There were a couple minor name changes, i.e. the function `group` was renamed to `groupBy`.
438Please consult the documentation.
440### Version 0.3 - June 26, 2018
442Version 0.3 of Squeal adds views as well as composite and enumerated types to Squeal.
443To support these features, a new kind `SchemumType` was added.
445```Haskell
446data SchemumType
447 = Table TableType
448 | View RelationType
449 | Typedef PGType
450```
452As a consequence, you will have to update your schema definitions like so:
454```Haskell
455-- Squeal 0.2
456type Schema =
457 '[ "users" :::
458 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
459 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
460 , "name" ::: 'NoDef :=> 'NotNull 'PGtext
461 ]
462 ]
464-- Squeal 0.3
465type Schema =
466 '[ "users" ::: 'Table (
467 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
468 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
469 , "name" ::: 'NoDef :=> 'NotNull 'PGtext
470 ])
471 ]
472```
474**Views**
476You can now create, drop, and query views.
478```Haskell
479>>> :{
480type ABC =
481 ('[] :: TableConstraints) :=>
482 '[ "a" ::: 'NoDef :=> 'Null 'PGint4
483 , "b" ::: 'NoDef :=> 'Null 'PGint4
484 , "c" ::: 'NoDef :=> 'Null 'PGint4
485 ]
486type BC =
487 '[ "b" ::: 'Null 'PGint4
488 , "c" ::: 'Null 'PGint4
489 ]
490:}
492>>> :{
493let
494 definition :: Definition '["abc" ::: 'Table ABC ] '["abc" ::: 'Table ABC, "bc" ::: 'View BC]
495 definition = createView #bc (select (#b :* #c :* Nil) (from (table #abc)))
496in printSQL definition
497:}
498CREATE VIEW "bc" AS SELECT "b" AS "b", "c" AS "c" FROM "abc" AS "abc";
500>>> :{
501let
502 definition :: Definition '["abc" ::: 'Table ABC, "bc" ::: 'View BC] '["abc" ::: 'Table ABC]
503 definition = dropView #bc
504in printSQL definition
505:}
506DROP VIEW "bc";
508>>> :{
509let
510 query :: Query '["abc" ::: 'Table ABC, "bc" ::: 'View BC] '[] BC
511 query = selectStar (from (view #bc))
512in printSQL query
513:}
514SELECT * FROM "bc" AS "bc"
515```
517**Enumerated Types**
519PostgreSQL has a powerful type system. It even allows for user defined types.
520For instance, you can define enumerated types which are data types that comprise
521a static, ordered set of values. They are equivalent to Haskell algebraic data
522types whose constructors are nullary. An example of an enum type might be the days of the week,
523or a set of status values for a piece of data.
525Enumerated types are created using the `createTypeEnum` command, for example:
527```Haskell
528>>> :{
529let
530 definition :: Definition '[] '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])]
531 definition = createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy" :* Nil)
532:}
533>>> printSQL definition
534CREATE TYPE "mood" AS ENUM ('sad', 'ok', 'happy');
535```
537Enumerated types can also be generated from a Haskell algebraic data type with nullary constructors, for example:
539```Haskell
540>>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
541>>> instance SOP.Generic Schwarma
542>>> instance SOP.HasDatatypeInfo Schwarma
544>>> :kind! EnumFrom Schwarma
545EnumFrom Schwarma :: PGType
546= 'PGenum '["Beef", "Lamb", "Chicken"]
548>>> :{
549let
550 definition :: Definition '[] '["schwarma" ::: 'Typedef (EnumFrom Schwarma)]
551 definition = createTypeEnumFrom @Schwarma #schwarma
552:}
553>>> printSQL definition
554CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');
555```
557You can express values of an enum type using `label`, which is an overloaded method
558of the `IsPGlabel` typeclass.
560```Haskell
561>>> :{
562let
563 expression :: Expression sch rels grp params ('NotNull (EnumFrom Schwarma))
564 expression = label @"Chicken"
565in printSQL expression
566:}
567'Chicken'
568```
570**Composite Types**
572In addition to enum types, you can define composite types.
573A composite type represents the structure of a row or record;
574it is essentially just a list of field names and their data types.
577`createTypeComposite` creates a composite type. The composite type is
578specified by a list of attribute names and data types.
580```Haskell
581>>> :{
582let
583 definition :: Definition '[] '["complex" ::: 'Typedef ('PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8])]
584 definition = createTypeComposite #complex (float8 `As` #real :* float8 `As` #imaginary :* Nil)
585:}
586>>> printSQL definition
587CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
588```
590Composite types are almost equivalent to Haskell record types.
591However, because of the potential presence of `NULL`
592all the record fields must be `Maybe`s of basic types.
593Composite types can be generated from a Haskell record type, for example:
595```Haskell
596>>> data Complex = Complex {real :: Maybe Double, imaginary :: Maybe Double} deriving GHC.Generic
597>>> instance SOP.Generic Complex
598>>> instance SOP.HasDatatypeInfo Complex
600>>> :kind! CompositeFrom Complex
601CompositeFrom Complex :: PGType
602= 'PGcomposite '['("real", 'PGfloat8), '("imaginary", 'PGfloat8)]
604>>> :{
605let
606 definition :: Definition '[] '["complex" ::: 'Typedef (CompositeFrom Complex)]
607 definition = createTypeCompositeFrom @Complex #complex
608in printSQL definition
609:}
610CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
611```
613A row constructor is an expression that builds a row value
614(also called a composite value) using values for its member fields.
616```Haskell
617>>> :{
618let
619 i :: Expression '[] '[] 'Ungrouped '[] ('NotNull (CompositeFrom Complex))
620 i = row (0 `As` #real :* 1 `As` #imaginary :* Nil)
621:}
622>>> printSQL i
623ROW(0, 1)
624```
626You can also use `(&)` to apply a field label to a composite value.
628```Haskell
629>>> :{
630let
631 expr :: Expression '[] '[] 'Ungrouped '[] ('Null 'PGfloat8)
632 expr = i & #imaginary
633in printSQL expr
634:}
635(ROW(0, 1)).imaginary
636```
638Both composite and enum types can be automatically encoded from and decoded to their equivalent Haskell types.
639And they can be dropped.
641```Haskell
642>>> :{
643let
644 definition :: Definition '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])] '[]
645 definition = dropType #mood
646:}
647>>> printSQL definition
648DROP TYPE "mood";
649```
651**Additional Changes**
653Squeal 0.3 also introduces a typeclass `HasAll` similar to `Has` but for a list of aliases.
654This makes it possible to clean up some unfortunately messy Squeal 0.2 definitions.
656```Haskell
657-- Squeal 0.2
658>>> unique (Column #a :* Column #b :* Nil)
660-- Squeal 0.3
661>>> unique (#a :* #b :* Nil)
662```
664Squeal 0.3 also adds `IsLabel` instances for `Aliased` expressions and tables as well as
665heterogeneous lists, allowing for some more economy of code.
667```Haskell
668-- Squeal 0.2 (or 0.3)
669>>> select (#a `As` #a :* Nil) (from (table (#t `As` #t)))
671-- Squeal 0.3
672>>> select #a (from (table #t))
673```
675Squeal 0.3 also fixes a bug that prevented joined queries on self-referencing tables.
677The above changes required major and minor changes to Squeal DSL functions.
678Please consult the documentation.
680### Version 0.2.1 - April 7, 2018
682This minor update fixes an issue where alias identifiers could conflict with
683reserved words in PostgreSQL. To fix the issue, alias identifiers are now
684quoted. Thanks to Petter Rasmussen for the fix.
686### Version 0.2 - March 26, 2018
688**Changes**
689- **Constraints** - Type level table constraints like primary and foreign keys and column constraints like having `DEFAULT`.
690- **Migrations** - Support for linear, invertible migrations tracked in an auxiliary table
691- **Arrays** - Support for fixed- and variable-length arrays
692- **Aliases** - Generalized `Has` constraint
693- **Pools** - Support for pooled connections
694- **Transactions** - Support for transaction control language
695- **Queries, Manipulations, Definitions** - Small and large changes to Squeal's DSL
697Well, a lot of things changed!
699**Constraints**
701An important request was to bring constraints to the type level.
702This means that more of the schema is statically known. In `0.1` column constraints - which boil down
703to having `DEFAULT` or not - were at the type level, but they were confusingly named.
705```haskell
7060.1: 'Optional ('NotNull 'PGInt4)
7070.2: 'Def :=> 'NotNull 'PGInt4
7080.1: 'Required ('NotNull 'PGInt4)
7090.2: 'NoDef :=> 'NotNull 'PGInt4
710```
712The `:=>` type operator is intended to helpfully connote a constraint relation.
713It's also used for table constraints which are new in `0.2`.
715```haskell
716"emails" :::
717 '[ "pk_emails" ::: 'PrimaryKey '["id"]
718 , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
719 ] :=>
720 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
721 , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
722 , "email" ::: 'NoDef :=> 'Null 'PGtext
723 ]
724```
726Another change in the constraint system was the removal of column constraints from
727query and manipulation results, as result columns don't support a notion of `DEFAULT`.
728This necessitates a distinction between `TableType`s which have both column and table
729constraints and `RelationType`s which have neither.
731**Migrations**
733Migrations are a hot topic and many people have requested this feature. Squeal `0.2`
734adds support for linear, invertible migrations. That is, a migration is a named,
735invertible, schema-tracking computation:
737```haskell
738data Migration io schema0 schema1 = Migration
739 { name :: Text -- ^ The `name` of a `Migration`.
740 -- Each `name` in a `Migration` should be unique.
741 , up :: PQ schema0 schema1 io () -- ^ The `up` instruction of a `Migration`.
742 , down :: PQ schema1 schema0 io () -- ^ The `down` instruction of a `Migration`.
743 }
744```
746And, `Migration`s can be put together in an "aligned" list:
748```haskell
749data AlignedList p x0 x1 where
750 Done :: AlignedList p x x
751 (:>>) :: p x0 x1 -> AlignedList p x1 x2 -> AlignedList p x0 x2
752```
754These aligned lists are free categories and might look familiar from
755the [reflections without remorse](http://okmij.org/ftp/Haskell/zseq.pdf) technique,
756which uses their cousins, aligned sequences.
758In the context of migration, they allow one to chain new migrations as a
759schema evolves over time. `Migration`s' execution is tracked in an auxiliary
760migrations table. Migration lists can then be run or rewinded.
762```haskell
763migrateUp
764 :: MonadBaseControl IO io
765 => AlignedList (Migration io) schema0 schema1 -- ^ migrations to run
766 -> PQ
767 ("schema_migrations" ::: MigrationsTable ': schema0)
768 ("schema_migrations" ::: MigrationsTable ': schema1)
769 io ()
771migrateDown
772 :: MonadBaseControl IO io
773 => AlignedList (Migration io) schema0 schema1 -- ^ migrations to rewind
774 -> PQ
775 ("schema_migrations" ::: MigrationsTable ': schema1)
776 ("schema_migrations" ::: MigrationsTable ': schema0)
777 io ()
778```
780**Aliases**
782In Squeal `0.1`, we had different typeclasses `HasColumn` and `HasTable` to indicate
783that a table has a column or that a schema has a table. In Squeal `0.2` this has been
784unified to a single typeclass,
786```haskell
787class KnownSymbol alias =>
788 Has (alias :: Symbol) (fields :: [(Symbol,kind)]) (field :: kind)
789 | alias fields -> field where
790```
792**Arrays**
794Support for array types has been added to Squeal `0.2` through
795the `'PGfixarray` and `'PGvararray` `PGType`s. Array values can be
796constructed using the `array` function and can be encoded from and decoded to
797Haskell `Vector`s.
799**Pools**
801Squeal `0.2` provides a monad transformer `PoolPQ` that's an instance of `MonadPQ`.
802The `resource-pool` library is leveraged to provide striped pools of `Connection`s.
803`PoolPQ` should be a drop in replacement for running `Manipulation`s and `Query`s with
804`PQ`.
806**Transactions**
808Squeal `0.2` supports a simple transaction control language. A computation in
809`MonadPQ` can be called `transactionally` with different levels of isolation.
810Additionally, a schema changing computation, a data definition, can be run in a
811transaction. Running a computation in a transaction means that all SQL statements
812will be rolled back if an exception is encountered.
814**Queries, Manipulations and Definitions**
816The above changes required major and minor changes to Squeal DSL functions.
817Please consult the documentation.