Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destroy all GADTs; Removes the From GADT and SqlExpr GADT #228

Merged
merged 33 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a8f8c87
Explode the From GADT. Move runFrom into the ToFrom typeclass removin…
belevy Nov 4, 2020
7b59829
Reorganize Experimental folder. Move Subquery into core Experimental.…
belevy Nov 9, 2020
9f6f9b3
Cleanup hackage documentation. Make sure stylish ran correctly. Updat…
belevy Nov 30, 2020
1ba08ab
Merge remote-tracking branch 'upstream/master' into explode-from-gadt
belevy Dec 4, 2020
89bd673
Update ERaw to change the direction of NeedParens (parent now tells c…
belevy Jan 17, 2021
8a9b586
Get rid of AliasedValue and ValueReference; added sqlExprMetaAlias to…
belevy Jan 17, 2021
4dc58ec
Remove EList and EEmptyList; ERaw is now technically possible in each…
belevy Jan 18, 2021
f77134e
Remove entity specific constructors from SqlExpr
belevy Jan 19, 2021
2da0526
Remove EOrderBy, EDistinctOn; Change PreprocessedFrom a to just be an…
belevy Jan 19, 2021
c9eb845
Remove EOrderByRandom, calling distinctOnOrderBy with rand will choke…
belevy Jan 19, 2021
ec85366
Remove ESet
belevy Jan 19, 2021
2f5ae76
Remove EInsert and EInsertFinal
belevy Jan 19, 2021
2ab733f
Make postgres tests pass
belevy Jan 19, 2021
2d09ae1
Change aliased val to be legal value by waiting until expr materializ…
belevy Jan 21, 2021
01407d2
Cleanup ToAliasRefernce; Add isReference meta to value reference even…
belevy Jan 21, 2021
e3ae687
Merge pull request #9 from foxhound-systems/final-expr
belevy Feb 5, 2021
c821b61
Expose Experimental submodules
belevy Feb 5, 2021
1ee1866
Update changelog
belevy Feb 5, 2021
9d1550b
Merge branch 'master' into explode-from-gadt
belevy Feb 5, 2021
7a579e9
Create a FromRaw to replace FromSubquery and FromIdent in from clause…
belevy Feb 8, 2021
dd8814e
Convert all of experimental to use new From type instead of From type…
belevy Feb 11, 2021
75619fe
Expose the new functions and fix the mysql test compilation error (ty…
belevy Feb 11, 2021
ae9ef12
Merge pull request #10 from foxhound-systems/from-raw
belevy Feb 11, 2021
4f9793f
Bump version and add more comments
belevy Feb 12, 2021
9bf3476
ValidOnClause was too restrictive, ToFrom is actually the correct amo…
belevy Feb 12, 2021
096c1ac
Unbreak lateral joins by introducing a completely different ValidOnCl…
belevy Feb 14, 2021
2e82a84
Merge branch 'master' into explode-from-gadt
belevy Feb 21, 2021
017b036
Fixe error introduced in merge with master
belevy Feb 21, 2021
189baf3
Merge branch 'master' into explode-from-gadt
belevy May 7, 2021
769418c
Dont realias alias references
belevy May 19, 2021
e34c1ba
Never realias an already aliased Entity or Value
belevy May 20, 2021
8bb6615
reindex value references to the latest 'source'
belevy May 20, 2021
8fd2d5d
Merge branch 'master' into explode-from-gadt
belevy May 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
3.4.1.0
3.5.0.0
=======-
- @belevy
- [#228](https://github.com/bitemyapp/esqueleto/pull/228)
- Destroy all GADTs; Removes the From GADT and SqlExpr GADT
- From GADT is replaced with a From typeclass
- From GADT is replaced with a From data type and FromRaw
- SqlExpr is now all defined in terms of ERaw
- Modified ERaw to contain a SqlExprMeta with any extra information
that may be needed
- Experimental top level is now strictly for documentation and all the
implementation details are in Experimental.* modules

3.4.1.0
=======
- @Vlix
- [#232](https://github.com/bitemyapp/esqueleto/pull/232)
- Export the `ValidOnClauseValue` type family
Expand Down
2 changes: 1 addition & 1 deletion esqueleto.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: 1.12

name: esqueleto
version: 3.4.1.0
version: 3.5.0.0
synopsis: Type-safe EDSL for SQL queries on persistent backends.
description: @esqueleto@ is a bare bones, type-safe EDSL for SQL queries that works with unmodified @persistent@ SQL backends. Its language closely resembles SQL, so you don't have to learn new concepts, just new syntax, and it's fairly easy to predict the generated SQL and optimize it for your backend. Most kinds of errors committed when writing SQL are caught as compile-time errors---although it is possible to write type-checked @esqueleto@ queries that fail at runtime.
.
Expand Down
82 changes: 42 additions & 40 deletions src/Database/Esqueleto/Experimental.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,24 @@ module Database.Esqueleto.Experimental

-- * Documentation

Table(..)
-- ** Basic Queries
from
, table
, from
, Table(..)
, SubQuery(..)
, selectQuery

-- ** Joins
, (:&)(..)
, on
, innerJoin
, innerJoinLateral
, leftJoin
, leftJoinLateral
, rightJoin
, fullOuterJoin
, crossJoin
, crossJoinLateral

-- ** Set Operations
-- $sql-set-operations
Expand All @@ -42,16 +53,7 @@ module Database.Esqueleto.Experimental
, with
, withRecursive

, innerJoin
, innerJoinLateral
, leftJoin
, leftJoinLateral
, rightJoin
, fullOuterJoin
, crossJoin
, crossJoinLateral

-- * Internals
-- ** Internals
, From(..)
, ToMaybe(..)
, ToAlias(..)
Expand All @@ -60,8 +62,8 @@ module Database.Esqueleto.Experimental
, ToAliasReferenceT
, ToSqlSetOperation(..)
, ValidOnClauseValue
-- * The Normal Stuff

-- * The Normal Stuff
, where_
, groupBy
, orderBy
Expand Down Expand Up @@ -174,6 +176,7 @@ module Database.Esqueleto.Experimental
, DistinctOn
, LockingKind(..)
, SqlString

-- ** Joins
, InnerJoin(..)
, CrossJoin(..)
Expand All @@ -182,7 +185,8 @@ module Database.Esqueleto.Experimental
, FullOuterJoin(..)
, JoinKind(..)
, OnClauseWithoutMatchingJoinException(..)
-- * SQL backend

-- ** SQL backend
, SqlQuery
, SqlExpr
, SqlEntity
Expand All @@ -196,22 +200,20 @@ module Database.Esqueleto.Experimental
, insertSelectCount
, (<#)
, (<&>)

-- ** Rendering Queries
, renderQueryToText
, renderQuerySelect
, renderQueryUpdate
, renderQueryDelete
, renderQueryInsertInto
-- * Internal.Language
-- * RDBMS-specific modules
-- $rdbmsSpecificModules

-- * Helpers
-- ** Helpers
, valkey
, valJ
, associateJoin

-- * Re-exports
-- ** Re-exports
-- $reexports
, deleteKey
, module Database.Esqueleto.Internal.PersistentImport
Expand Down Expand Up @@ -316,7 +318,7 @@ import Database.Esqueleto.Experimental.ToMaybe
--
-- @
-- select $ do
-- people <- from $ Table \@Person
-- people <- from $ table \@Person
-- where_ (people ^. PersonName ==. val \"John\")
-- pure people
-- @
Expand Down Expand Up @@ -344,8 +346,8 @@ import Database.Esqueleto.Experimental.ToMaybe
-- @
-- select $ do
-- (people :& blogPosts) <-
-- from $ Table \@Person
-- \`LeftOuterJoin\` Table \@BlogPost
-- from $ table \@Person
-- \`leftJoin\` table \@BlogPost
-- \`on\` (\\(people :& blogPosts) ->
-- people ^. PersonId ==. blogPosts ?. BlogPostAuthorId)
-- where_ (people ^. PersonAge >. val 18)
Expand Down Expand Up @@ -376,19 +378,19 @@ import Database.Esqueleto.Experimental.ToMaybe
--
-- In this version, with each successive 'on' clause, only the tables
-- we have already joined into are in scope, so we must pattern match
-- accordingly. In this case, in the second 'InnerJoin', we do not use
-- accordingly. In this case, in the second 'innerJoin', we do not use
-- the first `Person` reference, so we use @_@ as a placeholder to
-- ignore it. This prevents a possible runtime error where a table
-- is referenced before it appears in the sequence of 'JOIN's.
--
-- @
-- select $ do
-- (people1 :& followers :& people2) <-
-- from $ Table \@Person
-- \`InnerJoin` Table \@Follow
-- from $ table \@Person
-- \`innerJoin` table \@Follow
-- \`on\` (\\(people1 :& followers) ->
-- people1 ^. PersonId ==. followers ^. FollowFollowed)
-- \`InnerJoin` Table \@Person
-- \`innerJoin` table \@Person
-- \`on\` (\\(_ :& followers :& people2) ->
-- followers ^. FollowFollower ==. people2 ^. PersonId)
-- where_ (people1 ^. PersonName ==. val \"John\")
Expand Down Expand Up @@ -420,8 +422,8 @@ import Database.Esqueleto.Experimental.ToMaybe
-- peopleWithPosts <-
-- from $ do
-- (people :& blogPosts) <-
-- from $ Table \@Person
-- \`InnerJoin\` Table \@BlogPost
-- from $ table \@Person
-- \`innerJoin\` table \@BlogPost
-- \`on\` (\\(p :& bP) ->
-- p ^. PersonId ==. bP ^. BlogPostAuthorId)
-- groupBy (people ^. PersonId)
Expand Down Expand Up @@ -451,8 +453,8 @@ import Database.Esqueleto.Experimental.ToMaybe
-- (authors, blogPosts) <- from $
-- (do
-- (author :& blogPost) <-
-- from $ Table \@Person
-- \`InnerJoin\` Table \@BlogPost
-- from $ table \@Person
-- \`innerJoin\` table \@BlogPost
-- \`on\` (\\(a :& bP) ->
-- a ^. PersonId ==. bP ^. BlogPostAuthorId)
-- where_ (author ^. PersonId ==. val currentPersonId)
Expand All @@ -461,11 +463,11 @@ import Database.Esqueleto.Experimental.ToMaybe
-- \`union_\`
-- (do
-- (follow :& blogPost :& author) <-
-- from $ Table \@Follow
-- \`InnerJoin\` Table \@BlogPost
-- from $ table \@Follow
-- \`innerJoin\` table \@BlogPost
-- \`on\` (\\(f :& bP) ->
-- f ^. FollowFollowed ==. bP ^. BlogPostAuthorId)
-- \`InnerJoin\` Table \@Person
-- \`innerJoin\` table \@Person
-- \`on\` (\\(_ :& bP :& a) ->
-- bP ^. BlogPostAuthorId ==. a ^. PersonId)
-- where_ (follow ^. FollowFollower ==. val currentPersonId)
Expand All @@ -484,14 +486,14 @@ import Database.Esqueleto.Experimental.ToMaybe
-- @
-- select $ do
-- (salesPerson :& maxSaleAmount :& maxSaleCustomerName) <-
-- from $ Table \@SalesPerson
-- \`CrossJoin\` (\\salesPerson -> do
-- sales <- from $ Table \@Sale
-- from $ table \@SalesPerson
-- \`crossJoinLateral\` (\\salesPerson -> do
-- sales <- from $ table \@Sale
-- where_ $ sales ^. SaleSalesPersonId ==. salesPerson ^. SalesPersonId
-- pure $ max_ (sales ^. SaleAmount)
-- )
-- \`CrossJoin\` (\\(salesPerson :& maxSaleAmount) -> do
-- sales <- from $ Table \@Sale
-- \`crossJoinLateral\` (\\(salesPerson :& maxSaleAmount) -> do
-- sales <- from $ table \@Sale
-- where_ $ sales ^. SaleSalesPersonId ==. salesPerson ^. SalesPersonId
-- &&. sales ^. SaleAmount ==. maxSaleAmount
-- pure $ sales ^. SaleCustomerName)
Expand Down Expand Up @@ -539,12 +541,12 @@ import Database.Esqueleto.Experimental.ToMaybe
-- @
-- select $ from $
-- (do
-- a <- from Table @A
-- a <- from $ table @A
-- pure $ a ^. ASomeCol
-- )
-- \`union_\`
-- (do
-- b <- from Table @B
-- b <- from $ table @B
-- pure $ b ^. BSomeCol
-- )
-- @
Expand Down
56 changes: 46 additions & 10 deletions src/Database/Esqueleto/Experimental/From.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ import Database.Esqueleto.Internal.PersistentImport

-- | 'FROM' clause, used to bring entities into scope.
--
-- Internally, this function uses the `From` datatype and the
-- `From` typeclass. Unlike the old `Database.Esqueleto.from`,
-- this does not take a function as a parameter, but rather
-- a value that represents a 'JOIN' tree constructed out of
-- instances of `From`. This implementation eliminates certain
-- Internally, this function uses the `From` datatype.
-- Unlike the old `Database.Esqueleto.from`, this does not
-- take a function as a parameter, but rather a value that
-- represents a 'JOIN' tree constructed out of instances of `From`.
-- This implementation eliminates certain
-- types of runtime errors by preventing the construction of
-- invalid SQL (e.g. illegal nested-@from@).
from :: ToFrom a a' => a -> SqlQuery a'
Expand All @@ -44,23 +44,44 @@ from f = do
pure a

type RawFn = NeedParens -> IdentInfo -> (TLB.Builder, [PersistValue])

-- | Data type defining the "From" language. This should not
-- constructed directly in application code.
--
-- A @From@ is a SqlQuery which returns a reference to the result of calling from
-- and a function that produces a portion of a FROM clause. This gets passed to
-- the FromRaw FromClause constructor directly when converting
-- from a @From@ to a @SqlQuery@ using @from@
--
-- /Since: 3.5.0.0/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the syntax for this annotation is:

Suggested change
-- /Since: 3.5.0.0/
-- @since 3.5.0.0

newtype From a = From
{ unFrom :: SqlQuery (a, RawFn)}


-- | A helper class primarily designed to allow using @SqlQuery@ directly in
-- a From expression. This is also useful for embedding a @SqlSetOperation@,
-- as well as supporting backwards compatibility for the
-- data constructor join tree used prior to /3.5.0.0/
--
-- /Since: 3.5.0.0/
class ToFrom a r | a -> r where
toFrom :: a -> From r
instance ToFrom (From a) a where
toFrom = id

-- | Data type for bringing a Table into scope in a JOIN tree
--
-- @
-- select $ from $ Table \@People
-- @
{-# DEPRECATED Table "/Since: 3.5.0.0/ - use 'table' instead" #-}
data Table a = Table

instance PersistEntity ent => ToFrom (Table ent) (SqlExpr (Entity ent)) where
toFrom _ = table

-- | Bring a PersistEntity into scope from a table
--
-- @
-- select $ from $ table \@People
-- @
--
-- /Since: 3.5.0.0/
table :: forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table = From $ do
let ed = entityDef (Proxy @ent)
Expand All @@ -85,6 +106,21 @@ instance (SqlSelect a r, ToAlias a, ToAliasReference a) => ToFrom (SubQuery (Sql
instance (SqlSelect a r, ToAlias a, ToAliasReference a) => ToFrom (SqlQuery a) a where
toFrom = selectQuery

-- | Select from a subquery, often used in conjuction with joins but can be
-- used without any joins. Because @SqlQuery@ has a @ToFrom@ instance you probably
-- dont need to use this function directly.
--
-- @
-- select $
-- p <- from $
-- selectQuery do
-- p <- from $ table \@Person
-- limit 5
-- orderBy [ asc p ^. PersonAge ]
-- ...
-- @
--
-- /Since: 3.5.0.0/
selectQuery :: (SqlSelect a r, ToAlias a, ToAliasReference a) => SqlQuery a -> From a
selectQuery subquery = From $ do
-- We want to update the IdentState without writing the query to side data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ with query = do
-- @
-- select $ do
-- cte <- withRecursive
-- (do $
-- person <- from $ Table \@Person
-- (do
-- person <- from $ table \@Person
-- where_ $ person ^. PersonId ==. val personId
-- pure person
-- )
-- unionAll_
-- (\\self -> do $
-- (\\self -> do
-- (p :& f :& p2 :& pSelf) <- from self
-- \`InnerJoin\` $ Table \@Follow
-- \`innerJoin\` $ table \@Follow
-- \`on\` (\\(p :& f) ->
-- p ^. PersonId ==. f ^. FollowFollower)
-- \`InnerJoin\` $ Table \@Person
-- \`innerJoin\` $ table \@Person
-- \`on\` (\\(p :& f :& p2) ->
-- f ^. FollowFollowed ==. p2 ^. PersonId)
-- \`LeftOuterJoin\` self
-- \`leftJoin\` self
-- \`on\` (\\(_ :& _ :& p2 :& pSelf) ->
-- just (p2 ^. PersonId) ==. pSelf ?. PersonId)
-- where_ $ isNothing (pSelf ?. PersonId)
Expand Down
Loading