content: {-# LANGUAGE PatternSynonyms #-}
  
  -- | This module contains a new way (introduced in 3.3.3.0) of using @FROM@ in
  -- Haskell. The old method was a bit finicky and could permit runtime errors,
  -- and this new way is both significantly safer and much more powerful.
  --
  -- This syntax will become the default syntax exported from the library in
  -- version @3.6.0.0@. To use the old syntax, see "Database.Esqueleto.Legacy".
  module Database.Esqueleto.Experimental
      ( -- * Setup
        -- $setup
  
        -- * Introduction
        -- $introduction
  
        -- * A New Syntax
        -- $new-syntax
  
        -- * Documentation
  
      -- ** Basic Queries
        from
      , table
      , Table(..)
      , selectQuery
  
      -- ** Joins
      , (:&)(..)
      , on
      , innerJoin
      , innerJoinLateral
      , leftJoin
      , leftJoinLateral
      , rightJoin
      , fullOuterJoin
      , crossJoin
      , crossJoinLateral
  
        -- ** Set Operations
        -- $sql-set-operations
      , union_
      , unionAll_
      , except_
      , intersect_
  
        -- ** Common Table Expressions
      , with
      , withRecursive
  
        -- ** Internals
      , From(..)
      , ToMaybe(..)
      , ToAlias(..)
      , ToAliasReference(..)
      , ToSqlSetOperation(..)
      , SqlSelect
      , Nullable
  
      -- * The Normal Stuff
      , where_
      , groupBy
      , groupBy_
      , orderBy
      , asc
      , desc
      , limit
      , offset
  
      , distinct
      , distinctOn
      , don
      , distinctOnOrderBy
      , having
      , locking
      , forUpdate
      , forUpdateSkipLocked
  
      , (^.)
      , (?.)
  
      , val
      , isNothing
      , isNothing_
      , just
      , nothing
      , joinV
      , withNonNull
  
      , countRows
      , count
      , countDistinct
  
      , not_
      , (==.)
      , (>=.)
      , (>.)
      , (<=.)
      , (<.)
      , (!=.)
      , (&&.)
      , (||.)
  
      , between
      , (+.)
      , (-.)
      , (/.)
      , (*.)
  
      , round_
      , ceiling_
      , floor_
  
      , min_
      , max_
      , sum_
      , avg_
      , castNum
      , castNumM
  
      , coalesce
      , coalesceDefault
  
      , lower_
      , upper_
      , trim_
      , ltrim_
      , rtrim_
      , length_
      , left_
      , right_
  
      , like
      , ilike
      , (%)
      , concat_
      , (++.)
      , castString
  
      , subList_select
      , valList
      , justList
  
      , in_
      , notIn
      , exists
      , notExists
  
      , set
      , (=.)
      , (+=.)
      , (-=.)
      , (*=.)
      , (/=.)
  
      , case_
      , toBaseId
      , toBaseIdMaybe
      , fromBaseId
      , fromBaseIdMaybe
      , subSelect
      , subSelectMaybe
      , subSelectCount
      , subSelectForeign
      , subSelectList
      , subSelectUnsafe
      , ToBaseId(..)
      , when_
      , then_
      , else_
      , Value(..)
      , ValueList(..)
      , OrderBy
      , DistinctOn
      , LockingKind(..)
      , LockableEntity(..)
      , SqlString
  
        -- ** Joins
      , InnerJoin(..)
      , CrossJoin(..)
      , LeftOuterJoin(..)
      , RightOuterJoin(..)
      , FullOuterJoin(..)
      , JoinKind(..)
      , OnClauseWithoutMatchingJoinException(..)
        -- *** Join Helpers
      , getTable
      , getTableMaybe
      , GetFirstTable(..)
  
        -- ** SQL backend
      , SqlQuery
      , SqlExpr
      , SqlEntity
      , select
      , selectOne
      , selectSource
      , delete
      , deleteCount
      , update
      , updateCount
      , insertSelect
      , insertSelectCount
      , (<#)
      , (<&>)
  
      -- ** Rendering Queries
      , renderQueryToText
      , renderQuerySelect
      , renderQueryUpdate
      , renderQueryDelete
      , renderQueryInsertInto
  
      -- ** Helpers
      , valkey
      , valJ
      , associateJoin
  
        -- ** Re-exports
        -- $reexports
      , deleteKey
      , module Database.Esqueleto.Internal.PersistentImport
      ) where
  
  import Database.Esqueleto.Internal.Internal hiding (From, from, on)
  import Database.Esqueleto.Internal.PersistentImport
  
  import Database.Esqueleto.Experimental.From
  import Database.Esqueleto.Experimental.From.CommonTableExpression
  import Database.Esqueleto.Experimental.From.Join
  import Database.Esqueleto.Experimental.From.SqlSetOperation
  import Database.Esqueleto.Experimental.ToAlias
  import Database.Esqueleto.Experimental.ToAliasReference
  import Database.Esqueleto.Experimental.ToMaybe
  
  -- $setup
  --
  -- If you're already using "Database.Esqueleto", then you can get
  -- started using this module just by changing your imports slightly,
  -- as well as enabling the [TypeApplications](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#extension-TypeApplications) extension.
  --
  -- @
  -- {-\# LANGUAGE TypeApplications \#-}
  --
  -- ...
  --
  -- import Database.Esqueleto.Experimental
  -- @
  --
  -- Note: Prior to @esqueleto-3.3.4.0@, the @Database.Esqueleto.Experimental@
  -- module did not reexport @Data.Esqueleto@.
  
  ----------------------------------------------------------------------
  
  -- $introduction
  --
  -- This module is fully backwards-compatible extension to the @esqueleto@
  -- EDSL that expands subquery functionality and enables
  -- [SQL set operations](https://en.wikipedia.org/wiki/Set_operations_(SQL\))
  -- to be written directly in Haskell. Specifically, this enables:
  --
  --   * Subqueries in 'JOIN' statements
  --   * 'UNION'
  --   * 'UNION' 'ALL'
  --   * 'INTERSECT'
  --   * 'EXCEPT'
  --
  -- As a consequence of this, several classes of runtime errors are now
  -- caught at compile time. This includes missing 'on' clauses and improper
  -- handling of @Maybe@ values in outer joins.
  --
  -- This module can be used in conjunction with the main "Database.Esqueleto"
  -- module, but doing so requires qualified imports to avoid ambiguous
  -- definitions of 'on' and 'from', which are defined in both modules.
  --
  -- Below we will give an overview of how to use this module and the
  -- features it enables.
  
  ----------------------------------------------------------------------
  
  -- $new-syntax
  --
  -- This module introduces a new syntax that serves to enable the aforementioned
  -- features. This new syntax also changes how joins written in the @esqueleto@
  -- EDSL to more closely resemble the underlying SQL.
  --
  -- For our examples, we'll use a schema similar to the one in the Getting Started
  -- section of "Database.Esqueleto":
  --
  -- @
  -- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
  --   Person
  --     name String
  --     age Int Maybe
  --     deriving Eq Show
  --   BlogPost
  --     title String
  --     authorId PersonId
  --     deriving Eq Show
  --   Follow
  --     follower PersonId
  --     followed PersonId
  --     deriving Eq Show
  -- |]
  -- @
  --
  -- === Example 1: Simple select
  --
  -- Let's select all people who are named \"John\".
  --
  -- ==== "Database.Esqueleto":
  --
  -- @
  -- select $
  -- from $ \\people -> do
  -- where_ (people ^. PersonName ==. val \"John\")
  -- pure people
  -- @
  --
  -- ==== "Database.Esqueleto.Experimental":
  --
  -- @
  -- select $ do
  -- people <- from $ table \@Person
  -- where_ (people ^. PersonName ==. val \"John\")
  -- pure people
  -- @
  --
  --
  -- === Example 2: Select with join
  --
  -- Let's select all people and their blog posts who are over
  -- the age of 18.
  --
  -- ==== "Database.Esqueleto":
  --
  -- @
  -- select $
  -- from $ \\(people \`LeftOuterJoin\` blogPosts) -> do
  -- on (just (people ^. PersonId) ==. blogPosts ?. BlogPostAuthorId)
  -- where_ (people ^. PersonAge >. just (val 18))
  -- pure (people, blogPosts)
  -- @
  --
  -- ==== "Database.Esqueleto.Experimental":
  --
  -- Here we use the ':&' operator to pattern match against the joined tables.
  --
  -- @
  -- select $ do
  -- (people :& blogPosts) <-
  --     from $ table \@Person
  --     \`leftJoin\` table \@BlogPost
  --     \`on\` (\\(people :& blogPosts) ->
  --             just (people ^. PersonId) ==. blogPosts ?. BlogPostAuthorId)
  -- where_ (people ^. PersonAge >. just (val 18))
  -- pure (people, blogPosts)
  -- @
  --
  -- === Example 3: Select with multi-table join
  --
  -- Let's select all people who follow a person named \"John\", including
  -- the name of each follower.
  --
  -- ==== "Database.Esqueleto":
  --
  -- @
  -- select $
  -- from $ \\(
  --  people1
  --  \`InnerJoin\` followers
  --  \`InnerJoin\` people2
  -- ) -> do
  -- on (people1 ^. PersonId ==. followers ^. FollowFollowed)
  -- on (followers ^. FollowFollower ==. people2 ^. PersonId)
  -- where_ (people1 ^. PersonName ==. val \"John\")
  -- pure (followers, people2)
  -- @
  --
  -- ==== "Database.Esqueleto.Experimental":
  --
  -- 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
  -- 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
  --     \`on\` (\\(people1 :& followers) ->
  --             people1 ^. PersonId ==. followers ^. FollowFollowed)
  --     \`innerJoin` table \@Person
  --     \`on\` (\\(_ :& followers :& people2) ->
  --             followers ^. FollowFollower ==. people2 ^. PersonId)
  -- where_ (people1 ^. PersonName ==. val \"John\")
  -- pure (followers, people2)
  -- @
  --
  -- === Example 4: Counting results of a subquery
  --
  -- Let's count the number of people who have posted at least 10 posts
  --
  -- ==== "Database.Esqueleto":
  --
  -- @
  -- select $ pure $ subSelectCount $
  -- from $ \\(
  --   people
  --   \`InnerJoin\` blogPosts
  -- ) -> do
  -- on (people ^. PersonId ==. blogPosts ^. BlogPostAuthorId)
  -- groupBy (people ^. PersonId)
  -- having ((count $ blogPosts ^. BlogPostId) >. val 10)
  -- pure people
  -- @
  --
  -- ==== "Database.Esqueleto.Experimental":
  --
  -- @
  -- select $ do
  -- peopleWithPosts <-
  --   from $ do
  --     (people :& blogPosts) <-
  --       from $ table \@Person
  --       \`innerJoin\` table \@BlogPost
  --       \`on\` (\\(p :& bP) ->
  --               p ^. PersonId ==. bP ^. BlogPostAuthorId)
  --     groupBy (people ^. PersonId)
  --     having ((count $ blogPosts ^. BlogPostId) >. val 10)
  --     pure people
  -- pure $ count (peopleWithPosts ^. PersonId)
  -- @
  --
  -- We now have the ability to refactor this
  --
  -- === Example 5: Sorting the results of a UNION with limits
  --
  -- Out of all of the posts created by a person and the people they follow,
  -- generate a list of the first 25 posts, sorted alphabetically.
  --
  -- ==== "Database.Esqueleto":
  --
  -- Since 'UNION' is not supported, this requires using `Database.Esqueleto.rawSql`. (Not shown)
  --
  -- ==== "Database.Esqueleto.Experimental":
  --
  -- Since this module supports all set operations (see `SqlSetOperation`), we can use
  -- `Union` to write this query.
  --
  -- @
  -- select $ do
  -- (authors, blogPosts) <- from $
  --   (do
  --     (author :& blogPost) <-
  --       from $ table \@Person
  --       \`innerJoin\` table \@BlogPost
  --       \`on\` (\\(a :& bP) ->
  --               a ^. PersonId ==. bP ^. BlogPostAuthorId)
  --     where_ (author ^. PersonId ==. val currentPersonId)
  --     pure (author, blogPost)
  --   )
  --   \`union_\`
  --   (do
  --     (follow :& blogPost :& author) <-
  --       from $ table \@Follow
  --       \`innerJoin\` table \@BlogPost
  --       \`on\` (\\(f :& bP) ->
  --               f ^. FollowFollowed ==. bP ^. BlogPostAuthorId)
  --       \`innerJoin\` table \@Person
  --       \`on\` (\\(_ :& bP :& a) ->
  --               bP ^. BlogPostAuthorId ==. a ^. PersonId)
  --     where_ (follow ^. FollowFollower ==. val currentPersonId)
  --     pure (author, blogPost)
  --   )
  -- orderBy [ asc (blogPosts ^. BlogPostTitle) ]
  -- limit 25
  -- pure (authors, blogPosts)
  -- @
  --
  -- === Example 6: LATERAL JOIN
  --
  -- As of version @3.4.0.0@, lateral subquery joins are supported.
  --
  --
  -- @
  -- select $ do
  -- (salesPerson :& maxSaleAmount :& maxSaleCustomerName) <-
  --   from $ table \@SalesPerson
  --   \`crossJoinLateral\` (\\salesPerson -> do
  --         sales <- from $ table \@Sale
  --         where_ $ sales ^. SaleSalesPersonId ==. salesPerson ^. SalesPersonId
  --         pure $ max_ (sales ^. SaleAmount)
  --         )
  --   \`crossJoinLateral\` (\\(salesPerson :& maxSaleAmount) -> do
  --         sales <- from $ table \@Sale
  --         where_ $ sales ^. SaleSalesPersonId ==. salesPerson ^. SalesPersonId
  --              &&. sales ^. SaleAmount ==. maxSaleAmount
  --         pure $ sales ^. SaleCustomerName)
  --         )
  -- pure (salesPerson ^. SalesPersonName, maxSaleAmount, maxSaleCustomerName)
  -- @
  --
  -- This is the equivalent to the following SQL (example taken from the
  -- [MySQL Lateral Derived Table](https://dev.mysql.com/doc/refman/8.0/en/lateral-derived-tables.html)
  -- documentation):
  --
  -- @
  -- SELECT
  --   salesperson.name,
  --   max_sale.amount,
  --   max_sale_customer.customer_name
  -- FROM
  --   salesperson,
  --   -- calculate maximum size, cache it in transient derived table max_sale
  --   LATERAL
  --   (SELECT MAX(amount) AS amount
  --     FROM all_sales
  --     WHERE all_sales.salesperson_id = salesperson.id)
  --   AS max_sale,
  --   LATERAL
  --   (SELECT customer_name
  --     FROM all_sales
  --     WHERE all_sales.salesperson_id = salesperson.id
  --     AND all_sales.amount =
  --         -- the cached maximum size
  --         max_sale.amount)
  --   AS max_sale_customer;
  -- @
  
  -- $sql-set-operations
  --
  -- Data type that represents SQL set operations. This includes
  -- 'UNION', 'UNION' 'ALL', 'EXCEPT', and 'INTERSECT'. These types form
  -- a binary tree, with @SqlQuery@ values on the leaves.
  --
  -- Each function corresponding to the aforementioned set operations
  -- can be used as an infix in a @from@ to help with readability
  -- and lead to code that closely resembles the underlying SQL. For example,
  --
  -- @
  -- select $ from $
  --   (do
  --      a <- from $ table @A
  --      pure $ a ^. ASomeCol
  --   )
  --   \`union_\`
  --   (do
  --      b <- from $ table @B
  --      pure $ b ^. BSomeCol
  --   )
  -- @
  --
  -- is translated into
  --
  -- @
  -- SELECT * FROM (
  --   (SELECT a.some_col FROM a)
  --   UNION
  --   (SELECT b.some_col FROM b)
  -- )
  -- @
  --
  