packages feed

esqueleto 3.5.2.1 → 3.5.2.2

raw patch · 4 files changed

+121/−9 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

changelog.md view
@@ -1,3 +1,9 @@+3.5.2.2+=======+- @NikitaRazmakhnin+  - [#278](https://github.com/bitemyapp/esqueleto/pull/278)+        - Fix generating of bad sql using nexted expressions with `distinctOnOrderBy`.+ 3.5.2.1 ======= - @cdparks
esqueleto.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.12  name:           esqueleto-version:        3.5.2.1+version:        3.5.2.2 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.                 .
src/Database/Esqueleto/Internal/Internal.hs view
@@ -374,7 +374,11 @@     toDistinctOn :: SqlExpr OrderBy -> SqlExpr DistinctOn     toDistinctOn (ERaw m f) = ERaw m $ \p info ->         let (b, vals) = f p info-        in (TLB.fromLazyText $ head $ TL.splitOn " " $ TLB.toLazyText b, vals)+        in  ( TLB.fromLazyText+              $ TL.replace " DESC" ""+              $ TL.replace " ASC" ""+              $ TLB.toLazyText b+            , vals )  -- | @ORDER BY random()@ clause. --@@ -3664,15 +3668,111 @@     -> R.ReaderT backend m () deleteKey = Database.Persist.delete --- | Avoid N+1 queries and join entities into a map structure+-- | Avoid N+1 queries and join entities into a map structure.+--+-- This function is useful to call on the result of a single @JOIN@. For+-- example, suppose you have this query:+-- -- @--- getFoosAndNestedBarsFromParent :: ParentId -> (Map (Key Foo) (Foo, [Maybe (Entity Bar)]))--- getFoosAndNestedBarsFromParent parentId = 'fmap' associateJoin $ 'select' $--- 'from' $ \\(foo `'LeftOuterJoin`` bar) -> do---   'on' (bar '?.' BarFooId '==.' foo '^.' FooId)---   'where_' (foo '^.' FooParentId '==.' 'val' parentId)---   'pure' (foo, bar)+-- getFoosAndNestedBarsFromParent+--     :: ParentId+--     -> SqlPersistT IO [(Entity Foo, Maybe (Entity Bar))]+-- getFoosAndNestedBarsFromParent parentId =+--     'select' $ do+--         (foo :& bar) <- from $+--             table @Foo+--             ``LeftOuterJoin``+--             table @Bar+--                 ``on`` do+--                     \\(foo :& bar) ->+--                         foo ^. FooId ==. bar ?. BarFooId+--         where_ $+--             foo ^. FooParentId ==. val parentId+--         pure (foo, bar) -- @+--+-- This is a natural result type for SQL - a list of tuples. However, it's not+-- what we usually want in Haskell - each @Foo@ in the list will be represented+-- multiple times, once for each @Bar@.+--+-- We can write @'fmap' 'associateJoin'@ and it will translate it into a 'Map'+-- that is keyed on the 'Key' of the left 'Entity', and the value is a tuple of+-- the entity's value as well as the list of each coresponding entity.+--+-- @+-- getFoosAndNestedBarsFromParentHaskellese+--     :: ParentId+--     -> SqlPersistT (Map (Key Foo) (Foo, [Maybe (Entity Bar)]))+-- getFoosAndNestedBarsFromParentHaskellese parentId =+--     'fmap' 'associateJoin' $ getFoosdAndNestedBarsFromParent parentId+-- @+--+-- What if you have multiple joins?+--+-- Let's use 'associateJoin' with a *two* join query.+--+-- @+-- userPostComments+--     :: SqlQuery (SqlExpr (Entity User, Entity Post, Entity Comment))+-- userPostsComment = do+--     (u :& p :& c) <- from $+--         table @User+--         ``InnerJoin``+--         table @Post+--             `on` do+--                 \\(u :& p) ->+--                     u ^. UserId ==. p ^. PostUserId+--         ``InnerJoin``+--         table @Comment+--             ``on`` do+--                 \\(_ :& p :& c) ->+--                     p ^. PostId ==. c ^. CommentPostId+--     pure (u, p, c)+-- @+--+-- This query returns a User, with all of the users Posts, and then all of the+-- Comments on that post.+--+-- First, we *nest* the tuple.+--+-- @+-- nest :: (a, b, c) -> (a, (b, c))+-- nest (a, b, c) = (a, (b, c))+-- @+--+-- This makes the return of the query conform to the input expected from+-- 'associateJoin'.+--+-- @+-- nestedUserPostComments+--     :: SqlPersistT IO [(Entity User, (Entity Post, Entity Comment))]+-- nestedUserPostComments =+--     fmap nest $ select userPostsComments+-- @+--+-- Now, we can call 'associateJoin' on it.+--+-- @+-- associateUsers+--     :: [(Entity User, (Entity Post, Entity Comment))]+--     -> Map UserId (User, [(Entity Post, Entity Comment)])+-- associateUsers =+--     associateJoin+-- @+--+-- Next, we'll use the 'Functor' instances for 'Map' and tuple to call+-- 'associateJoin' on the @[(Entity Post, Entity Comment)]@.+--+-- @+-- associatePostsAndComments+--     :: Map UserId (User, [(Entity Post, Entity Comment)])+--     -> Map UserId (User, Map PostId (Post, [Entity Comment]))+-- associatePostsAndComments =+--     fmap (fmap associateJoin)+-- @+--+-- For more reading on this topic, see+-- <https://www.foxhound.systems/blog/grouping-query-results-haskell/ this Foxhound Systems blog post>. -- -- @since 3.1.2 associateJoin
test/PostgreSQL/Test.hs view
@@ -220,6 +220,12 @@       slightlyLessSimpleTest $ \bp ->         distinctOnOrderBy [asc (bp ^. BlogPostAuthorId), asc (bp ^. BlogPostTitle)] +    itDb "generates correct sql with nested expression (distinctOnOrderBy)" $ do+      let query = do+            let orderVal = coalesce [nothing, just $ val (10 :: Int)]+            distinctOnOrderBy [ asc orderVal, desc orderVal ] $ pure orderVal+      select query+      asserting noExceptions