All Versions
Latest Version
Avg Release Cycle
65 days
Latest Release
1325 days ago

Changelog History

  • v0.7.0.1 Changes

    October 01, 2020

    🔖 Version 0.7

    Thanks to Samuel Schlesinger, Adam Wespiser, Cullin Poreski,
    Matthew Doty and Mark Wotton for tons of contributions.
    🔖 Version 0.7 of Squeal makes many changes.

    Inter-schema Foreign Key Bug
    Unfortunately, there was a bug in inter-schema foreign keys in previous
    🔖 versions of Squeal. Essentially, it was erroneously assumed that
    foreign keys always point to tables in the public schema. To remedy this
    the ForeignKey type has changed kind from

    \>\>\> :kind 'ForeignKey 'ForeignKey :: [Symbol] -\> Symbol -\> [Symbol] -\> TableConstraint


    \>\>\> :kind 'ForeignKey 'ForeignKey :: [Symbol] -\> Symbol -\> Symbol -\> [Symbol] -\> TableConstraint

    ⬆️ To upgrade your database schemas type, you will have to change, e.g.

    'ForeignKey '["foo\_id1", "foo\_id2"] "foo" '["id1", "id2"]


    'ForeignKey '["foo\_id1", "foo\_id2"] "public" "foo" '["id1", "id2"]

    Locking Clauses

    You can now add row level locking clauses to your select queries

    Polymorphic Lateral Contexts

    Previously, lateral contexts which are used for lateral joins
    and subquery expressions had to have monomorphic lateral contexts,
    which greatly reduced composability of queries involving lateral
    🛠 joins. Squeal 0.7 fixes this limitation, making it possible to
    have polymorphic lateral context! When looking up a column heretofore,
    the relevant typeclasses would search through Join lat from.
    This is the "correct" ordering as far as the structure from
    left to right in the query, making lat consistently ordered as
    one goes through nested lateral joins or nested subquery expressions.
    However, it doesn't really matter how the lookup orders the columns.
    And if the lookup searches through Join from lat instead then thanks
    to good old Haskell lazy list appending, if a query only references
    columns in from then it will work no matter the lat.
    With a small proviso; if you leave lat polymorphic,
    then you must qualify all columns since there could be more than
    one table even if from has only one table in it.


    The DecodeRow Monad now has a MonadFail instance.

    🆕 New row decoder combinators have been added. The functions
    🏗 appendRows and consRow let you build row decoders up
    from pieces.

    Previously, Squeal made it easy to decode enum types to Haskell
    enum types (sum types with nullary constructors) so long as
    the Haskell type exactly matches the enum type. However, because
    of limitations in Haskell - constructors must be capitalized,
    name conflicts are often disambiguated with extra letters, etc -
    it's often the case that their constructors won't exactly match the
    Postgres enum type's labels. The new function enumValue allows
    to define typesafe custom enum decoders, similar to how rowValue
    👍 allows to define typesafe custom composite decoders.

    \>\>\> :{data Dir = North | East | South | Westinstance IsPG Dir wheretype PG Dir = 'PGenum '["north", "south", "east", "west"]instance FromPG Dir where fromPG = enumValue $ label @"north" North :\* label @"south" South :\* label @"east" East :\* label @"west" West:}


    🆕 New DDL statements have been added allowing to rename and
    reset the schema of different schemum objects. Also, new DDL statements
    have been added for adding comments to schemum objects.


    👍 Squeal now supports procedure definitions and calls.

    cmdTuples and cmdStatus

    The cmdTuples and cmdStatus functions from LibPQ are now

    PQ Monad Instances

    the PQ Monad has been given instances for MonadCatch,
    MonadThrow, MonadMask, MonadBase, MonadBaseControl, and

    Referential Actions

    A new type ReferentialAction has been factored out of
    ⚡️ OnDeleteClauses and OnUpdateClauses. And Missing actions,
    0️⃣ SetNotNull and SetDefault are now included.

    ⬆️ To upgrade, change from e.g. OnDeleteCascade to OnDelete Cascade.

    Array functions

    🛠 Squeal now offers typesafe indexing for fixed length arrays and matrices,
    with new functions index1 and index2. And new functions arrAny
    and arrAll have been added to enable comparisons to any or all elements
    of a variable length array.


    ⚡️ Tables being manipulated are now re-aliasable, and updates can reference
    "from" clauses, actually called UsingClauses in Squeal, similar to deletes.

    Other changes
    🆕 New tests and bugfixes have been added. More support for encoding and decoding
    of different types has been added. Time values now use iso8601 formatting
    👷 for inlining. Also, the GitHub repo has moved from using Circle CI to using
    ✅ GitHub Actions for continuous integration testing.

  • v0.6.0.2 Changes

    April 23, 2020

    🛠 Fix documentation for defaultMode for transactions.

  • v0.6.0.1 Changes

    April 16, 2020

    📚 Documentation fixes.

  • v0.6.0.0 Changes

    April 16, 2020

    🔖 Version 0.6

    🔖 Version 0.6 makes a number of large changes and additions to Squeal.
    I want to thank folks who contributed issues and pull requests;
    ilyakooo0, tuomohopia, league, Raveline, Sciencei, mwotton, and more.

    I particularly would like to thank my employer SimSpace and colleagues.
    We are actively using Squeal at SimSpace which has pushed its development.

    My colleague Mark Wotton has also created a project
    squealgen to generate
    a Squeal schema directly from the database which is awesome.

    Module hierarchy

    Squeal had been growing some rather large modules, whereas I prefer
    sub-thousand line modules. Accordingly, I split up the module
    hierarchy further. This means there's 60 modules which looks a little
    overwhelming, but I think it makes it easier to locate functionality.
    It also makes working in a single module less overwhelming.
    All relevant functionality is still being exported by Squeal.PostgreSQL.

    Statement Profunctors

    Squeal's top level queries and manipulations left something to be desired.
    Because Query_ and Manipulation_ were type families, they could be
    a bit confusing to use. For instance,

    \>\>\> :{selectUser :: Query\_ DB UserId UserselectUser = select\_ (#id `as` #userId :\* #name `as` #userName) (from (table #users) & where\_ (#id .== param @1)):}\>\>\> :t selectUser selectUser :: Query'[]'[] '["public" ::: '["users" ::: 'Table ('[] :=\> UsersColumns)]] '['NotNull 'PGint4] '["userId" ::: 'NotNull 'PGint4, "userName" ::: 'NotNull 'PGtext]

    So the UserId and User types are completely replaced by corresponding
    Postgres types. This means that the query can be run, for instance,
    with any parameter that is a generic singleton container of Int32.
    We've lost apparent type safety. You could accidentally run selectUser
    with a WidgetId parameter instead of a UserId and it could typecheck.

    That's because Query is a pure SQL construct, with no knowledge for
    how to encode or decode Haskell values.

    Another annoyance of Query_ and Manipulation_ is that they must
    be applied to Haskell types which exactly match their corresponding
    Postgres types. So, in practice, you often end up with one-off
    data type definitions just to have a type that exactly matches,
    having the same field names, and the same ordering, etc. as the
    returned row.

    Both of these issues are solved with the new Statement type. Let's
    👀 see its definition.

    data Statement db x y whereManipulation:: (SOP.All (OidOfNull db) params, SOP.SListI row) =\> EncodeParams db params x-\> DecodeRow row y-\> Manipulation '[] db params row-\> Statement db x yQuery:: (SOP.All (OidOfNull db) params, SOP.SListI row) =\> EncodeParams db params x-\> DecodeRow row y-\> Query '[] '[] db params row-\> Statement db x y

    👀 You can see that a Statement bundles either a Query or a Manipulation
    together with a way to EncodeParams and a way to DecodeRow. This
    ties the statement to actual Haskell types. Going back to the example,

    \>\>\> :{selectUser :: Statement DB UserId UserselectUser = query $ select\_ (#id `as` #userId :\* #name `as` #userName) (from (table #users) & where\_ (#id .== param @1)):}

    Now we really do have the type safety of only being able to executeParams
    selectUser with a UserId parameter. Here we've used the smart
    constructor query which automatically uses the generic instances of
    UserId and User to construct a way to EncodeParams and a way to
    DecodeRow. We can use the Query constructor to do custom encodings
    and decodings.

    \>\>\> :{selectUser :: Statement DB UserId (UserId, Text) selectUser = Query enc dec sql where enc = contramap getUserId aParam dec = do uid \<- #id uname \<- #name return (uid, uname) sql = select Star (from (table #users) & where\_ (#id .== param @1)):}

    EncodeParams and DecodeRow both have convenient APIs. EncodeParams
    is Contravariant and can be composed with combinators. DecodeRow
    is a Monad and has IsLabel instances. Since Statements bundle
    both together, they form Profunctors, where you can lmap over
    parameters and rmap over rows.

    The Statement Profunctor is heavily influenced by
    the Statement Profunctor from Nikita Volkov's excellent hasql library,
    🏗 building on the use of postgresql-binary for encoding and decoding.


    Many Haskell types have corresponding Postgres types like Double
    corresponds to float8. Squeal makes this an open relationship with the
    PG type family. Squeal 0.6 makes it easy to generate PG of your
    Haskell types, though you might have to turn on -XUndecidableInstances,
    by deriving an IsPG instance.
    In addition to having a corresponding Postgres type,
    to fully embed your Haskell type you want instances of ToPG db to
    encode your type as an out-of-line parameter, FromPG to
    decode your type from a result value, and Inline to inline
    values of your type directly in SQL statements.

    \>\>\> :{newtype CustomerId = CustomerId {getCustomerId :: Int32}deriving newtype (IsPG, ToPG db, FromPG, Inline):}\>\>\> :kind! PG CustomerIdPG CustomerId :: PGType= 'PGint4

    You can even embed your Haskell records and enum types using
    deriving via.

    \>\>\> :{data Complex = Complex {real :: Double, imaginary :: Double}deriving stock (GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving (IsPG, ToPG db, FromPG, Inline) via Composite Complex:}\>\>\> :kind! PG ComplexPG Complex :: PGType= 'PGcomposite '["real" ::: 'NotNull 'PGfloat8, "imaginary" ::: 'NotNull 'PGfloat8]\>\>\> printSQL (inline (Complex 0 1))ROW((0.0 :: float8), (1.0 :: float8))\>\>\> :{data Answer = Yes | Noderiving stock (GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving (IsPG, ToPG db, FromPG, Inline) via Enumerated Answer:}\>\>\> :kind! PG AnswerPG Answer :: PGType= 'PGenum '["Yes", "No"]\>\>\> printSQL (inline Yes) 'Yes'

    You can also embed your types encoded as Json or Jsonb.

    \>\>\> :{data Foo = Bar Int | Baz Char Textderiving stock (GHC.Generic) deriving anyclass (ToJSON, FromJSON) deriving (IsPG, ToPG db, FromPG, Inline) via Jsonb Foo:}\>\>\> :kind! PG FooPG Foo :: PGType= 'PGjsonb\>\>\> printSQL (inline (Baz 'a' "aaa")) ('{"tag":"Baz","contents":["a","aaa"]}' :: jsonb)

    One thing to notice about ToParam db is that it has an
    extra parameter db that the other classes don't have. That's
    because for some types, such as arrays and composites, you
    need to know the OID of the element types in order to unambiguously
    encode those types. And if the element types are user defined,
    then they have to be looked up in the database. The extra parameter
    lets us look through the schema for a matching type, and then
    look up that type's OID.


    Previously Squeal migrations could be either pure, involving only
    data definitions, or impure, allowing arbitrary IO. But, they
    had to be rewindable; that is, every migration step had to have
    an inverse. Squeal 0.6 generalizes to allow both invertible and
    🆓 one-way migrations. The core datatype for migrations, the free
    📦 category Path has been moved to its own package free-categories.


    Squeal 0.6 enables filtering and ordering for aggregate
    arguments and filtering for window function arguments.

    arrayAgg (All #col & orderBy [AscNullsFirst #col] & filterWhere (#col .\< 100))

    ⬆️ To upgrade existing code, if you have an aggregate with multiple arguments,
    👉 use Alls instead of All or Distincts instead of Distinct
    🏁 and if you have a window function, apply either Window or Windows
    to its argument(s). Additionally, convenient functions allNotNull and
    distinctNotNull safely filter out NULL.


    Squeal 0.6 adds both Haskell and corresponding Postgres range types.

    data Bound x= Infinite -- ^ unbounded | Closed x -- ^ inclusive | Open x -- ^ exclusivedata Range x = Empty | NonEmpty (Bound x) (Bound x)(\<=..\<=), (\<..\<), (\<=..\<), (\<..\<=) :: x -\> x -\> Range xmoreThan, atLeast, lessThan, atMost :: x -\> Range xsingleton :: x -\> Range xwhole :: Range x

    Indexes and functions

    👍 Squeal 0.6 adds support for creating and dropping user defined
    indexes and functions to your schema, which can then be used
    in statements.

    Lateral joins

    👍 Squeal 0.6 adds support for lateral joins, which may reference previous

    Null handling

    Some null handling functions were added such as monoNotNull
    and unsafeNotNull. Because Squeal is aggressively NULL polymorphic,
    sometimes inference errors can occur. You can apply monoNotNull
    to fix something to be not NULL. You can apply unsafeNotNull
    when you know that something can't be NULL, for instance if you've
    filtered NULL out of a column.

    Other changes

    👍 Lots of other things changed. Literal and literal are now called Inline and inline. ColumnConstraint is called Optionality. NullityTypes are called NullTypes. Squeal 0.6 adds support for domain types. It more carefully types
    CREATE _ IF NOT EXISTS and DROP _ IF EXISTS definitions. The
    🔨 Exception type was refactored to remove Maybes and new pattern
    synonyms were defined to easily match on a few common SQL errors.
    VarChar and FixChar types were added with smart constructors.
    ✅ Many bugs were fixed. Also, many more tests were added and
    a new benchmark suite. A lot more things were changed that I've
    probably forgotten about.

  • v0.5.2.0 Changes

    January 06, 2020

    🛠 Bugfix.

    🌐 Previously connection pools were handled by defining a PoolPQ Monad with a MonadPQ instance. Turns out this was sort of dumb. What it did was run each query by taking a connection from the pool, running the query, and then returning the connection to the pool. This is not what is usually expected in a real use case. In a real use case, like a web service, you probably want to use a single connection to serve an endpoint when it's called.

  • v0.5.1

    November 12, 2019