5Version 0.5 makes a number of large changes and additions to Squeal.
6I want to thank folks who contributed issues and pull requests;
7Raveline, rimmington, ilyakooo0, chshersh, reygoch, and more.
9**Multi-schema support**
11Previous versions of Squeal only supported definitions in the "public"
12schema. Squeal 0.5 adds support for multiple schemas with the new kind
15type SchemasType = [(Symbol,SchemaType)]
18In order to upgrade your queries, manipulations and definitions from
19Squeal 0.4, you will have to apply the `Public` type family, which indicates
20that your only schema is the "public" schema.
24setup :: Definition '[] Schema
27setup :: Definition (Public '[]) (Public Schema)
30You can create non-public schemas using `createSchema` and inversely
31remove them using `dropSchema`. There is also an idempotent
32`createSchemaIfNotExists`.
34In order to handle aliases which may refer to non-public schemas, Squeal 0.5
35introduces `QualifiedAlias`es. A `QualifiedAlias` can be referred to using
36the `(!)` operator from the `IsQualified` typeclass; but if you want to refer
37to an alias from the "public" schema, you can continue to use a single
38overloaded label, no need to write `#public ! #tab`, just write `#tab`.
40**Top-level statements**
42As a consequence of multi-schema support, common table expressions that
43a `Query` or `Manipulation` may refer to had to be broken into a new parameter.
44Additionally, `Query` gained another new parameter for its outer scope which
45will be discussed in the next section.
47To simplify type signatures that you have to write for top-level queries
48and manipulations, Squeal 0.5 introduces the `Query_` and `Manipulation_`
53 (schemas :: SchemasType)
56 Query_ schemas params row =
57 Query '[] '[] schemas (TuplePG params) (RowPG row)
60As you see, a top-level `Query_` has no outer scope and no common
61table expressions in scope, they are empty `'[]`. Moreover,
62its parameters and row parameters are now `Type`s, which are
63converted to corresponding Postgres types using the `TuplePG`
64and `RowPG` type families. This means you can write the type signatures
65for your top-level statements using the Haskell types that you
66intend to input and retrieve when running the statements. Similar
67to `Query_`, there is a `Manipulation_` type, the only difference
68being that a general `Manipulation` doesn't have an outer scope,
69only a scope for common table expressions.
71**Subquery expressions**
73Previous versions of Squeal offered subquery expression support
74but there were issues. The common use case of using the `IN` operator
75from SQL was overly complex. There was a proliferation of subquery
76functions to handle different comparison operators, each with two versions
77for comparing `ALL` or `ANY` of the query rows. And, most confoundedly,
78there was no way to support the `EXISTS` subquery because the "outer scope"
79for a query was not available.
81Squeal adds an outer scope to the `Query` type.
87 (schemas :: SchemasType)
88 (params :: [NullityType])
90 = UnsafeQuery { renderQuery :: ByteString }
93This enables a well-typed `exists` function.
97 :: Query (Join outer from) commons schemas params row
98 -> Condition outer commons grp schemas params from
101The `in_` and `notIn` functions were simplified to handle the most
102common use case of comparing to a list of values.
106 :: Expression outer commons grp schemas params from ty -- ^ expression
107 -> [Expression outer commons grp schemas params from ty]
108 -> Condition outer commons grp schemas params from
111Finally, general subquery comparisons were abstracted to work with
112any comparison operators, using the `Operator` type which will be discussed in
117 :: Expression outer commons grp schemas params from ty1
118 -> Operator ty1 ty2 ('Null 'PGbool)
119 -> Query (Join outer from) commons schemas params '[col ::: ty2]
120 -> Condition outer commons grp schemas params from
123 :: Expression outer commons grp schemas params from ty1
124 -> Operator ty1 ty2 ('Null 'PGbool)
125 -> Query (Join outer from) commons schemas params '[col ::: ty2]
126 -> Condition outer commons grp schemas params from
129**Expression RankNTypes**
131Squeal 0.5 introduces RankNType type synonyms for common expression patterns.
132The simplest example is the `Expr` type, a type for "closed" expressions
133that cannot reference any aliases or parameters.
137 = forall outer commons grp schemas params from
138 . Expression outer commons grp schemas params from x
141There is also a function type `(-->)`, which is a subtype of the usual Haskell function
146 = forall outer commons grp schemas params from
147 . Expression outer commons grp schemas params from x
148 -> Expression outer commons grp schemas params from y
151We saw in the subquery section that there is an `Operator` type.
155 = forall outer commons grp schemas params from
156 . Expression outer commons grp schemas params from x1
157 -> Expression outer commons grp schemas params from x2
158 -> Expression outer commons grp schemas params from y
161There are also types `FunctionN` and `FunctionVar` for n-ary functions
162and variadic functions.
166 = forall outer commons grp schemas params from
167 . NP (Expression outer commons grp schemas params from) xs
168 -> Expression outer commons grp schemas params from y
171An n-ary function takes an `NP` list of `Expression`s as its argument.
172Squeal 0.5 adds a helpful operator `(*:)`, to help scrap your `Nil`s.
173You can construct an `NP` list now by using the operator `(:*)`,
174until your last element, using `(*:)` there. For instance, the function
175`atan2_` takes two arguments, `atan2_ (pi *: 2)`.
179Previously, Squeal provided a couple versions of `SELECT` depending
180on whether you wanted to select all columns of a unique table
181in the from clause, i.e. `*`, all columns of a particular table
182in the from clause, i.e. `.*`, or a list of columns from the from clause.
184Squeal 0.5 refactors this pattern into a `Selection` GADT type, allowing
185for abstraction and combinations that were not possible before.
188data Selection outer commons grp schemas params from row where
190 :: HasUnique tab from row
191 => Selection outer commons 'Ungrouped schemas params from row
195 -> Selection outer commons 'Ungrouped schemas params from row
198 => NP (Aliased (Expression outer commons grp schemas params from)) row
199 -> Selection outer commons grp schemas params from row
202 => NP (Aliased (WindowFunction outer commons grp schemas params from)) row
203 -> WindowDefinition outer commons grp schemas params from
204 -> Selection outer commons grp schemas params from row
206 :: Selection outer commons grp schemas params from right
207 -> Selection outer commons grp schemas params from left
208 -> Selection outer commons grp schemas params from (Join left right)
211In addition to the `Star`, `DotStar` and `List` constructors, there is an
212`Also` constructor combinator, which enables users to combine `Selection`s.
213Additionally, there is an `Over` constructor that is used to enable
214window functions, described in the next section.
216To upgrade from Squeal 0.4, you will replace `selectStar` with `select Star`,
217replace `selectDotStar #tab` with `select (DotStar #tab)`. The `List`
218selection is such a common use case that there is a function `select_` which
219automatically applies it, so to upgrade from Squeal 0.4, replace `select`
220with `select_`. There are also independent `selectDistinct` and `selectDistinct_`
221functions to filter out duplicate rows.
225Previously, Squeal provided a couple versions of `INSERT` depending
226on whether you wanted to insert `VALUES` or a query.
228Squeal 0.5 refactors this pattern into a `QueryClause` GADT.
232insertRow_ #tab (Set 2 `as` #col1 :* Default `as` #col2)
233insertQuery_ #tab (selectStar (from (table #other_tab)))
236insertInto_ #tab (Values_ (Set 2 `as` #col1 :* Default `as` #col2))
237insertInto_ #tab (Subquery (select Star (from (table #other_tab))))
240**Window functions and aggregation**
242Previous versions of Squeal provided no support for window functions.
243Squeal 0.5 adds support for window functions. We saw `Over` in the
249 => NP (Aliased (WindowFunction outer commons grp schemas params from)) row
250 -> WindowDefinition outer commons grp schemas params from
251 -> Selection outer commons grp schemas params from row
254`Over` combines window functions with window definitions. A `WindowDefinition`
255is constructed using the `partitionBy` function, optionally with an `orderBy`
259query :: Query_ (Public Schema) () (Row Int32 Int32)
261 (#col1 & Also (rank `as` #col2 `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
265Here the `rank` function is a `WindowFunction`
268rank :: WinFun0 ('NotNull 'PGint8)
269rank = UnsafeWindowFunction "rank()"
272`WinFun0` is a RankNType, like `Expr` was, used for no argument window functions.
273Similarly, there are also `WinFun1` and `WinFunN` types for
274window functions and n-ary window functions.
276In order to use the same syntax for certain window functions and aggregate functions,
277a new typeclass `Aggregate` was introduced. So for instance, `sum_` can be
278either a `Distinction` `Expression` or a `WindowFunction`, used either with
279a `groupBy` or with a `partitionBy` in the appropriate way.
282data Distinction (expr :: kind -> Type) (ty :: kind)
287Aggregate functions can be run over all rows or only over distinct rows,
288while window functions bear no such distinction.
292Thanks to [https://github.com/Raveline](Raveline) Squeal 0.5 introduces
293a function `defaultMain` to easily create a command line program to
294run or rewind your migrations.
296Previously, Squeal's migrations were impure, allowing in addition to
297running `Definition`s to run arbitrary `IO` operations, such as inserting
298data into the database or printing out status messages. In order to
299provide compatibility with other migration systems, Squeal 0.5 introduces
300pure migrations, that is migrations that are only `Definition`s.
303data Migration p schemas0 schemas1 = Migration
304 { name :: Text -- ^ The `name` of a `Migration`.
305 -- Each `name` in a `Migration` should be unique.
306 , up :: p schemas0 schemas1 -- ^ The `up` instruction of a `Migration`.
307 , down :: p schemas1 schemas0 -- ^ The `down` instruction of a `Migration`.
308 } deriving (GHC.Generic)
311A pure migration is a `Migration Definition`, a pair of inverse
312`Definition`s with a unique name. To recover impure migrations, Squeal 0.5
313introduces the `Terminally` type.
316newtype Terminally trans monad x0 x1 = Terminally
317 { runTerminally :: trans x0 x1 monad () }
320`Terminally` applies the indexed monad transformer and the monad it transforms
321to the unit type `()`, thereby turning an indexed monad into a `Category`. An
322impure migration is a `Migration (Terminally PQ IO)`. You can always cast
323a pure migration into an impure migration with the functor, `pureMigration`.
327 :: Migration Definition schemas0 schemas1
328 -> Migration (Terminally PQ IO) schemas0 schemas1
331To run either pure or impure migrations, Squeal 0.5 introduces
332a typeclass, `Migratory`.
335class Category p => Migratory p where
338 :: AlignedList (Migration p) schemas0 schemas1
339 -> PQ schemas0 schemas1 IO ()
342 :: AlignedList (Migration p) schemas0 schemas1
343 -> PQ schemas1 schemas0 IO ()
346The signatures of `migrateUp` and `migrateDown` have been changed
347to make them easier to compose with other `PQ` actions.
351You can now run transactions `ephemerally`, guaranteed to roll back,
352but return the result or throw the exception that the transaction
353would have generated. This is useful for testing. You can also
354`transactionallyRetry` computations, retrying the transaction if
355a [serialization failure](https://www.postgresql.org/docs/11/transaction-iso.html#XACT-REPEATABLE-READ) occurs.
359Squeal now supports money via a `Money` Haskell `Type` and a `'PGmoney`
360`PGType`. Squeal 0.5 also adds support for creating domain types
365Squeal 0.5 adds support for new functions; `now`, `makeDate`, `makeTime`,
366, `makeTimestamp`, and `makeTimestamptz`, a function `interval_` for
367constructing time intervals out multiples of `TimeUnit`s, and a new
368`TimeOp` class, defining affine space operators for time types and their
373Squeal allows you to include Haskell values in your statements using
374out of line `parameter`s, but often you want to include them inline,
375as a SQL `literal`. Squeal 0.5 enables this using the `Literal` class.
377**Set returning functions**
379Squeal 0.5 adds support for set returning functions such as `unnest`,
380with the `RankNType` `SetOfFunction`, which can be used as as a `FromClause`.
384Squeal 0.5 adds extensive support for text search types, functions and operators.
388Lots more changes went into and under the hood of the new version.
389The `Expression` module was split into coherent submodules since it
390had grown to immense proportions. New modules `Alias`, `PG` and `List`
391were added to relieve some of the burden that the `Schema` module
392had been carrying. Rendering has been better unified with a new
393`RenderSQL` typeclass. Type level list concatenation with the `Additional`
394typeclass has been added. `Manipulation`s `update` and `delete`
395were upgraded, so you can leave out fields you don't want to update and
396use `USING` clauses in deletes. Upserts which were previously broken
397now work. The `IO` typeclass hierarchy was changed from `MonadBase`
398and `MonadBaseControl` to `MonadIO` and `MonadUnliftIO`. A new
399`withRecursive` `Manipulation` was added. `SquealException`s were
400refactored and a `trySqueal` function added. Arrays were refactored.
401And there was probably more I've forgotten.
405Version 0.4 strengthens Squeal's type system, adds
406support for multidimensional arrays, improves support
407for container type, improves `with` statements,
408improves runtime exceptions, accomodates SQL's three-valued logic,
409adds subquery expressions, and adds table and view type expressions.
413Squeal 0.4 renames some kinds to aid intuition:
416type RowType = [(Symbol, NullityType)] -- previously RelationType
417type FromType = [(Symbol, RowType)] -- previously RelationsType
420Null safety for array and composite types is gained by having the base
421type of an array be a `NullityType` and the base type of a composite
427 | PGvararray NullityType
428 | PGfixarray Nat NullityType
429 | PGcomposite RowType
432Squeal embeds Postgres types into Haskell using data kinds and type-in-type:
435data PGType = PGbool | ..
436data NullityType = Null PGType | NotNull PGType
437type RowType = [(Symbol, NullityType)] -- previously RelationType
438type FromType = [(Symbol, RowType)] -- previously RelationsType
441In another sense, we can embed Haskell types
442into Postgres types by providing type families:
445type family PG (hask :: Type) :: PGType
446type family NullPG (hask :: Type) :: NullityType
447type family TuplePG (hask :: Type) :: [NullityType]
448type family RowPG (hask :: Type) :: RowType
451Let's look at these one by one.
453`PG` was introduced in Squeal 0.3. It was a closed type family that
454associates some Haskell types to their obvious corresponding Postgres
455types like `PG Double = 'PGfloat8`. It only worked on base types,
456no arrays or composites. Squeal 0.4 extends it to
457such container types and makes it an open type family so that
458users can make their own type instances.
460`NullPG` had a different name before but it does the obvious
461thing for Haskell with `Maybe`s:
464type family NullPG hask where
465 NullPG (Maybe hask) = 'Null (PG hask)
466 NullPG hask = 'NotNull (PG hask)
469`TuplePG` uses generics to turn tuple types (including records)
470into lists of `NullityType`s in the logical way, e.g.
471`TuplePG (Bool, Day) = '[ 'PGbool, 'PGdate]`.
473`RowPG` also uses generics to turn record types into a `RowType` in the logical way, e.g.
476>>> data Person = Person { name :: Text, age :: Int32 } deriving GHC.Generic
477>>> instance SOP.Generic Person
478>>> instance SOP.HasDatatypeInfo Person
479>>> :kind! TuplePG Person
480TuplePG Person :: [NullityType]
481= '['NotNull 'PGtext, 'NotNull 'PGint4]
482>>> :kind! RowPG Person
483RowPG Person :: [(Symbol, NullityType)]
484= '["name" ::: 'NotNull 'PGtext, "age" ::: 'NotNull 'PGint4]
487We've already seen a hint of why these types are useful in one construction
488from Squeal 0.3. Creating composite types in Postgres directly from a Haskell
489record type essentially uses `RowPG`. Another important use is in simplifying
490the type signatures for a `Query` or `Manipulation`. Very often, you will have
491a tuple type corresponding to the parameters and a record type corresponding
492to the returned columns of a `Query` or `Manipulation`. Instead of writing
493boilerplate signature you can reuse these with the help of `TuplePG` and `RowPG`
500 query :: Query '["user" ::: 'View (RowPG Person)] (TuplePG (Only Int32)) (RowPG Person)
501 query = selectStar (from (view #user) & where_ (#age .> param @1))
507In addition to being able to encode and decode basic Haskell types
508like `Int16` and `Text`, Squeal 0.4 permits you to encode and decode Haskell types to
509Postgres array types. The `Vector` type corresponds to to variable length arrays.
510And thanks to an idea from [Mike Ledger](https://github.com/mikeplus64),
511homogeneous tuples correspond to fixed length arrays. We can even
512create multi-dimensional fixed length arrays. Let's see an example.
517 { col1 :: Vector Int16
518 , col2 :: (Maybe Int16,Maybe Int16)
519 , col3 :: ((Int16,Int16),(Int16,Int16),(Int16,Int16))
520 } deriving (Eq, GHC.Generic)
523>>> instance Generic Row
524>>> instance HasDatatypeInfo Row
527Define a simple round trip query.
532 roundTrip :: Query '[] (TuplePG Row) (RowPG Row)
533 roundTrip = values_ $
534 parameter @1 (int2 & vararray) `as` #col1 :*
535 parameter @2 (int2 & fixarray @2) `as` #col2 :*
536 parameter @3 (int2 & fixarray @2 & fixarray @3) `as` #col3
539>>> :set -XOverloadedLists
540>>> let input = Row [1,2] (Just 1,Nothing) ((1,2),(3,4),(5,6))
542void . withConnection "host=localhost port=5432 dbname=exampledb" $ do
543 result <- runQueryParams roundTrip input
544 Just output <- firstRow result
545 liftBase . print $ input == output
552Squeal aims to provide a correspondence between Haskell types and Postgres types.
553In particular, Haskell ADTs with nullary constructors can correspond to
554Postgres enum types and Haskell record types can correspond to Postgres
555composite types. However, it's not always obvious that that's how a user
556will choose to store values of those types. So Squeal 0.4 introduces newtypes
557whose purpose is to specify how a user wants to store values of a type.
560newtype Json hask = Json {getJson :: hask}
561newtype Jsonb hask = Jsonb {getJsonb :: hask}
562newtype Composite record = Composite {getComposite :: record}
563newtype Enumerated enum = Enumerated {getEnumerated :: enum}
569>>> data Schwarma = Beef | Lamb | Chicken deriving (Eq, Show, GHC.Generic)
570>>> instance SOP.Generic Schwarma
571>>> instance SOP.HasDatatypeInfo Schwarma
573>>> data Person = Person {name :: Text, age :: Int32} deriving (Eq, Show, GHC.Generic)
574>>> instance SOP.Generic Person
575>>> instance SOP.HasDatatypeInfo Person
576>>> instance Aeson.FromJSON Person
577>>> instance Aeson.ToJSON Person
580We can create the equivalent Postgres types directly from their Haskell types.
585 '[ "schwarma" ::: 'Typedef (PG (Enumerated Schwarma))
586 , "person" ::: 'Typedef (PG (Composite Person))
592 setup :: Definition '[] Schema
594 createTypeEnumFrom @Schwarma #schwarma >>>
595 createTypeCompositeFrom @Person #person
599Let's demonstrate how to associate our Haskell types `Schwarma` and `Person`
600with enumerated, composite or json types in Postgres. First create a Haskell
601`Row` type using the `Enumerated`, `Composite` and `Json` newtypes as fields.
606 { schwarma :: Enumerated Schwarma
607 , person1 :: Composite Person
608 , person2 :: Json Person
609 } deriving (Eq, GHC.Generic)
612>>> instance Generic Row
613>>> instance HasDatatypeInfo Row
618 (Composite (Person "Faisal" 24))
619 (Json (Person "Ahmad" 48))
623Once again, define a round trip query.
628 roundTrip :: Query Schema (TuplePG Row) (RowPG Row)
629 roundTrip = values_ $
630 parameter @1 (typedef #schwarma) `as` #schwarma :*
631 parameter @2 (typedef #person) `as` #person1 :*
632 parameter @3 json `as` #person2
636Finally, we can drop our type definitions.
641 teardown :: Definition Schema '[]
642 teardown = dropType #schwarma >>> dropType #person
652 result <- runQueryParams roundTrip input
653 Just output <- firstRow result
654 liftBase . print $ input == output
656 void . withConnection "host=localhost port=5432 dbname=exampledb" $
659 & pqThen (define teardown)
666Squeal 0.3 supported WITH statements but there's a couple problems with them.
667Here's the type signature for `with` in Squeal 0.3.
671 :: SOP.SListI commons
672 => NP (Aliased (Manipulation schema params)) (common ': commons)
673 -- ^ common table expressions
674 -> Manipulation (With (common ': commons) schema) params results
675 -> Manipulation schema params results
678The first problem is that `with` only works with `Manipulations`.
679It can work on `Query`s by using `queryStatement` but it still will
680return a `Manipulation`. We can fix this issue by making `with` a
681method of a type class with instances for both `Query` and `Manipulation`.
683The second problem is that all the common table expressions
684refer the base schema, whereas in SQL, each subsequent CTE can
685refer to previous CTEs as well. But this can be fixed! First define a datatype:
688data CommonTableExpression statement params schema0 schema1 where
689 CommonTableExpression
690 :: Aliased (statement schema params) (alias ::: cte)
691 -> CommonTableExpression statement params schema (alias ::: 'View cte ': schema)
694It's just a wrapper around an aliased statement, where the statement
695could be either a `Query` or `Manipulation`, but it augments the schema
696by adding a view to it. It almost looks like a morphism between schemas
697but there is no way yet to compose them. Luckily, Squeal already has
698a datatype for this, which is used for migrations, the `AlignedList`
699type which is really the "free category". So we can then define a `With` type class:
702class With statement where
704 :: AlignedList (CommonTableExpression statement params) schema0 schema1
705 -> statement schema1 params row
706 -> statement schema0 params row
709By 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.
711Here's an example of using `with` for a `Query` and a `Manipulation`:
718 '[ "c1" ::: 'NotNull 'PGtext
719 , "c2" ::: 'NotNull 'PGtext] ]
721 '[ "c1" ::: 'NotNull 'PGtext
722 , "c2" ::: 'NotNull 'PGtext ]
724 selectStar (from (view #t1)) `as` #t2 :>>
725 selectStar (from (view #t2)) `as` #t3
726 ) (selectStar (from (view #t3)))
729WITH "t2" AS (SELECT * FROM "t1" AS "t1"), "t3" AS (SELECT * FROM "t2" AS "t2") SELECT * FROM "t3" AS "t3"
731>>> type ProductsTable = '[] :=> '["product" ::: 'NoDef :=> 'NotNull 'PGtext, "date" ::: 'Def :=> 'NotNull 'PGdate]
735 manipulation :: Manipulation
736 '[ "products" ::: 'Table ProductsTable
737 , "products_deleted" ::: 'Table ProductsTable
738 ] '[ 'NotNull 'PGdate] '[]
740 (deleteFrom #products (#date .< param @1) ReturningStar `as` #deleted_rows)
741 (insertQuery_ #products_deleted (selectStar (from (view (#deleted_rows `as` #t)))))
742in printSQL manipulation
744WITH "deleted_rows" AS (DELETE FROM "products" WHERE ("date" < ($1 :: date)) RETURNING *) INSERT INTO "products_deleted" SELECT * FROM "deleted_rows" AS "t"
747**Three Valued Logic**
749In previous versions of Squeal, conditions followed classical two valued logic
750of `true` and `false`.
754type Condition schema from grouping params =
755 Expression schema from grouping params ('NotNull 'PGbool)
758I had thought that three valued logic, which is what SQL uses, was confusing.
759However, multiple users reported being confused at being forced to do `NULL`
760handling, particularly in their left joins. Since the original motivation
761of being less confusing evaporated I decided to switch to three valued logic
762of `true`, `false` and `null_`.
766type Condition schema from grouping params =
767 Expression schema from grouping params ('Null 'PGbool)
770**Subquery Expressions**
772Squeal 0.4 adds support for subquery expressions such as `IN` and `op ANY/ALL`.
776Squeal 0.4 adds functions to define type expressions from tables and views
777and a type expression for user defined types, `typetable`, `typeview` and
780**Runtime Exceptions**
782Squeal now has an exception type which gives details on the sort of error
783encountered and handler functions.
788 { sqlExecStatus :: LibPQ.ExecStatus
789 , sqlStateCode :: Maybe ByteString
790 -- ^ https://www.postgresql.org/docs/current/static/errcodes-appendix.html
791 , sqlErrorMessage :: Maybe ByteString
793 | ResultException Text
794 | ParseException Text
796instance Exception SquealException
799 :: MonadBaseControl IO io
801 -> (SquealException -> io a) -- ^ handler
805 :: MonadBaseControl IO io
806 => (SquealException -> io a) -- ^ handler
810**Additional Changes**
812Squeal 0.4 adds `field` and `index` functions to get components of composite
813and array expressions.
815Squeal 0.4 adds a dependency on `records-sop` to offload a lot of boilerplate
816type family logic that was needed for `RowPG`.
818The above changes required major and minor changes to Squeal DSL functions.
819Please consult the documentation.
821### Version 0.3.2 - August 4, 2018
823Version 0.3.2 features extensive support for `JSON` functionality with
824more than 50 new functions.
825This work is entirely due to [Mike Ledger](https://github.com/mikeplus64)
826who has been making terrific contributions to Squeal. Thanks!
827We also got some examples in the documentation for pools submitted by
828[Raveline](https://github.com/Raveline). I'm so pleased to be
829getting pull requests and issue submissions from you all!
831### Version 0.3.1 - July 7, 2018
833Version 0.3.1 of Squeal enables the "Scrap your Nils" trick for
834heterogeneous lists of `Alias`es, `Aliased` expressions, `PGlabel`s and `By`s
835with the typeclasses `IsLabel`, `IsQualified`, `IsPGlabel`,
836and the new `Aliasable` typeclass, to eliminate all need of using `Nil` in a list.
837There were a couple minor name changes, i.e. the function `group` was renamed to `groupBy`.
838Please consult the documentation.
840### Version 0.3 - June 26, 2018
842Version 0.3 of Squeal adds views as well as composite and enumerated types to Squeal.
843To support these features, a new kind `SchemumType` was added.
852As a consequence, you will have to update your schema definitions like so:
858 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
859 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
860 , "name" ::: 'NoDef :=> 'NotNull 'PGtext
866 '[ "users" ::: 'Table (
867 '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
868 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
869 , "name" ::: 'NoDef :=> 'NotNull 'PGtext
876You can now create, drop, and query views.
881 ('[] :: TableConstraints) :=>
882 '[ "a" ::: 'NoDef :=> 'Null 'PGint4
883 , "b" ::: 'NoDef :=> 'Null 'PGint4
884 , "c" ::: 'NoDef :=> 'Null 'PGint4
887 '[ "b" ::: 'Null 'PGint4
888 , "c" ::: 'Null 'PGint4
894 definition :: Definition '["abc" ::: 'Table ABC ] '["abc" ::: 'Table ABC, "bc" ::: 'View BC]
895 definition = createView #bc (select (#b :* #c :* Nil) (from (table #abc)))
896in printSQL definition
898CREATE VIEW "bc" AS SELECT "b" AS "b", "c" AS "c" FROM "abc" AS "abc";
902 definition :: Definition '["abc" ::: 'Table ABC, "bc" ::: 'View BC] '["abc" ::: 'Table ABC]
903 definition = dropView #bc
904in printSQL definition
910 query :: Query '["abc" ::: 'Table ABC, "bc" ::: 'View BC] '[] BC
911 query = selectStar (from (view #bc))
914SELECT * FROM "bc" AS "bc"
919PostgreSQL has a powerful type system. It even allows for user defined types.
920For instance, you can define enumerated types which are data types that comprise
921a static, ordered set of values. They are equivalent to Haskell algebraic data
922types whose constructors are nullary. An example of an enum type might be the days of the week,
923or a set of status values for a piece of data.
925Enumerated types are created using the `createTypeEnum` command, for example:
930 definition :: Definition '[] '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])]
931 definition = createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy" :* Nil)
933>>> printSQL definition
934CREATE TYPE "mood" AS ENUM ('sad', 'ok', 'happy');
937Enumerated types can also be generated from a Haskell algebraic data type with nullary constructors, for example:
940>>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
941>>> instance SOP.Generic Schwarma
942>>> instance SOP.HasDatatypeInfo Schwarma
944>>> :kind! EnumFrom Schwarma
945EnumFrom Schwarma :: PGType
946= 'PGenum '["Beef", "Lamb", "Chicken"]
950 definition :: Definition '[] '["schwarma" ::: 'Typedef (EnumFrom Schwarma)]
951 definition = createTypeEnumFrom @Schwarma #schwarma
953>>> printSQL definition
954CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');
957You can express values of an enum type using `label`, which is an overloaded method
958of the `IsPGlabel` typeclass.
963 expression :: Expression sch rels grp params ('NotNull (EnumFrom Schwarma))
964 expression = label @"Chicken"
965in printSQL expression
972In addition to enum types, you can define composite types.
973A composite type represents the structure of a row or record;
974it is essentially just a list of field names and their data types.
977`createTypeComposite` creates a composite type. The composite type is
978specified by a list of attribute names and data types.
983 definition :: Definition '[] '["complex" ::: 'Typedef ('PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8])]
984 definition = createTypeComposite #complex (float8 `As` #real :* float8 `As` #imaginary :* Nil)
986>>> printSQL definition
987CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
990Composite types are almost equivalent to Haskell record types.
991However, because of the potential presence of `NULL`
992all the record fields must be `Maybe`s of basic types.
993Composite types can be generated from a Haskell record type, for example:
996>>> data Complex = Complex {real :: Maybe Double, imaginary :: Maybe Double} deriving GHC.Generic
997>>> instance SOP.Generic Complex
998>>> instance SOP.HasDatatypeInfo Complex
1000>>> :kind! CompositeFrom Complex
1001CompositeFrom Complex :: PGType
1002= 'PGcomposite '['("real", 'PGfloat8), '("imaginary", 'PGfloat8)]
1006 definition :: Definition '[] '["complex" ::: 'Typedef (CompositeFrom Complex)]
1007 definition = createTypeCompositeFrom @Complex #complex
1008in printSQL definition
1010CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
1013A row constructor is an expression that builds a row value
1014(also called a composite value) using values for its member fields.
1019 i :: Expression '[] '[] 'Ungrouped '[] ('NotNull (CompositeFrom Complex))
1020 i = row (0 `As` #real :* 1 `As` #imaginary :* Nil)
1026You can also use `(&)` to apply a field label to a composite value.
1031 expr :: Expression '[] '[] 'Ungrouped '[] ('Null 'PGfloat8)
1032 expr = i & #imaginary
1035(ROW(0, 1)).imaginary
1038Both composite and enum types can be automatically encoded from and decoded to their equivalent Haskell types.
1039And they can be dropped.
1044 definition :: Definition '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])] '[]
1045 definition = dropType #mood
1047>>> printSQL definition
1051**Additional Changes**
1053Squeal 0.3 also introduces a typeclass `HasAll` similar to `Has` but for a list of aliases.
1054This makes it possible to clean up some unfortunately messy Squeal 0.2 definitions.
1058>>> unique (Column #a :* Column #b :* Nil)
1061>>> unique (#a :* #b :* Nil)
1064Squeal 0.3 also adds `IsLabel` instances for `Aliased` expressions and tables as well as
1065heterogeneous lists, allowing for some more economy of code.
1068-- Squeal 0.2 (or 0.3)
1069>>> select (#a `As` #a :* Nil) (from (table (#t `As` #t)))
1072>>> select #a (from (table #t))
1075Squeal 0.3 also fixes a bug that prevented joined queries on self-referencing tables.
1077The above changes required major and minor changes to Squeal DSL functions.
1078Please consult the documentation.
1080### Version 0.2.1 - April 7, 2018
1082This minor update fixes an issue where alias identifiers could conflict with
1083reserved words in PostgreSQL. To fix the issue, alias identifiers are now
1084quoted. Thanks to Petter Rasmussen for the fix.
1086### Version 0.2 - March 26, 2018
1089- **Constraints** - Type level table constraints like primary and foreign keys and column constraints like having `DEFAULT`.
1090- **Migrations** - Support for linear, invertible migrations tracked in an auxiliary table
1091- **Arrays** - Support for fixed- and variable-length arrays
1092- **Aliases** - Generalized `Has` constraint
1093- **Pools** - Support for pooled connections
1094- **Transactions** - Support for transaction control language
1095- **Queries, Manipulations, Definitions** - Small and large changes to Squeal's DSL
1097Well, a lot of things changed!
1101An important request was to bring constraints to the type level.
1102This means that more of the schema is statically known. In `0.1` column constraints - which boil down
1103to having `DEFAULT` or not - were at the type level, but they were confusingly named.
11060.1: 'Optional ('NotNull 'PGInt4)
11070.2: 'Def :=> 'NotNull 'PGInt4
11080.1: 'Required ('NotNull 'PGInt4)
11090.2: 'NoDef :=> 'NotNull 'PGInt4
1112The `:=>` type operator is intended to helpfully connote a constraint relation.
1113It's also used for table constraints which are new in `0.2`.
1117 '[ "pk_emails" ::: 'PrimaryKey '["id"]
1118 , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
1120 '[ "id" ::: 'Def :=> 'NotNull 'PGint4
1121 , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
1122 , "email" ::: 'NoDef :=> 'Null 'PGtext
1126Another change in the constraint system was the removal of column constraints from
1127query and manipulation results, as result columns don't support a notion of `DEFAULT`.
1128This necessitates a distinction between `TableType`s which have both column and table
1129constraints and `RelationType`s which have neither.
1133Migrations are a hot topic and many people have requested this feature. Squeal `0.2`
1134adds support for linear, invertible migrations. That is, a migration is a named,
1135invertible, schema-tracking computation:
1138data Migration io schema0 schema1 = Migration
1139 { name :: Text -- ^ The `name` of a `Migration`.
1140 -- Each `name` in a `Migration` should be unique.
1141 , up :: PQ schema0 schema1 io () -- ^ The `up` instruction of a `Migration`.
1142 , down :: PQ schema1 schema0 io () -- ^ The `down` instruction of a `Migration`.
1146And, `Migration`s can be put together in an "aligned" list:
1149data AlignedList p x0 x1 where
1150 Done :: AlignedList p x x
1151 (:>>) :: p x0 x1 -> AlignedList p x1 x2 -> AlignedList p x0 x2
1154These aligned lists are free categories and might look familiar from
1155the [reflections without remorse](http://okmij.org/ftp/Haskell/zseq.pdf) technique,
1156which uses their cousins, aligned sequences.
1158In the context of migration, they allow one to chain new migrations as a
1159schema evolves over time. `Migration`s' execution is tracked in an auxiliary
1160migrations table. Migration lists can then be run or rewinded.
1164 :: MonadBaseControl IO io
1165 => AlignedList (Migration io) schema0 schema1 -- ^ migrations to run
1167 ("schema_migrations" ::: MigrationsTable ': schema0)
1168 ("schema_migrations" ::: MigrationsTable ': schema1)
1172 :: MonadBaseControl IO io
1173 => AlignedList (Migration io) schema0 schema1 -- ^ migrations to rewind
1175 ("schema_migrations" ::: MigrationsTable ': schema1)
1176 ("schema_migrations" ::: MigrationsTable ': schema0)
1182In Squeal `0.1`, we had different typeclasses `HasColumn` and `HasTable` to indicate
1183that a table has a column or that a schema has a table. In Squeal `0.2` this has been
1184unified to a single typeclass,
1187class KnownSymbol alias =>
1188 Has (alias :: Symbol) (fields :: [(Symbol,kind)]) (field :: kind)
1189 | alias fields -> field where
1194Support for array types has been added to Squeal `0.2` through
1195the `'PGfixarray` and `'PGvararray` `PGType`s. Array values can be
1196constructed using the `array` function and can be encoded from and decoded to
1201Squeal `0.2` provides a monad transformer `PoolPQ` that's an instance of `MonadPQ`.
1202The `resource-pool` library is leveraged to provide striped pools of `Connection`s.
1203`PoolPQ` should be a drop in replacement for running `Manipulation`s and `Query`s with
1208Squeal `0.2` supports a simple transaction control language. A computation in
1209`MonadPQ` can be called `transactionally` with different levels of isolation.
1210Additionally, a schema changing computation, a data definition, can be run in a
1211transaction. Running a computation in a transaction means that all SQL statements
1212will be rolled back if an exception is encountered.
1214**Queries, Manipulations and Definitions**
1216The above changes required major and minor changes to Squeal DSL functions.
1217Please consult the documentation.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list