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

Jnm/add cte materialized to postgres #417

Merged
merged 5 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
- [#341](https://github.com/bitemyapp/esqueleto/pull/341/)
- Add functions for `NULLS FIRST` and `NULLS LAST` in the Postgresql
module
- @JoelMcCracken
- [#354](https://github.com/bitemyapp/esqueleto/pull/354), [#417](https://github.com/bitemyapp/esqueleto/pull/417)
- Add `withMaterialized`, `withNotMaterialized` to the PostgreSQL module

3.5.13.2
========
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import Database.Esqueleto.Internal.Internal hiding (From(..), from, on)
-- PostgreSQL 12, non-recursive and side-effect-free queries may be inlined and
-- optimized accordingly if not declared @MATERIALIZED@ to get the previous
-- behaviour. See [the PostgreSQL CTE documentation](https://www.postgresql.org/docs/current/queries-with.html#id-1.5.6.12.7),
-- section Materialization, for more information.
-- section Materialization, for more information. To use a @MATERIALIZED@ query
-- in Esquelto, see functions 'withMaterialized' and 'withRecursiveMaterialized'.
--
-- /Since: 3.4.0.0/
with :: ( ToAlias a
Expand All @@ -50,7 +51,7 @@ with query = do
aliasedValue <- toAlias ret
let aliasedQuery = Q $ W.WriterT $ pure (aliasedValue, sideData)
ident <- newIdentFor (DBName "cte")
let clause = CommonTableExpressionClause NormalCommonTableExpression ident (\info -> toRawSql SELECT info aliasedQuery)
let clause = CommonTableExpressionClause NormalCommonTableExpression (\_ _ -> "") ident (\info -> toRawSql SELECT info aliasedQuery)
Q $ W.tell mempty{sdCteClause = [clause]}
ref <- toAliasReference ident aliasedValue
pure $ From $ do
Expand Down Expand Up @@ -107,7 +108,8 @@ withRecursive baseCase unionKind recursiveCase = do
ref <- toAliasReference ident aliasedValue
let refFrom = From (pure (ref, (\_ info -> (useIdent info ident, mempty))))
let recursiveQuery = recursiveCase refFrom
let clause = CommonTableExpressionClause RecursiveCommonTableExpression ident
let noModifier _ _ = ""
let clause = CommonTableExpressionClause RecursiveCommonTableExpression noModifier ident
(\info -> (toRawSql SELECT info aliasedQuery)
<> ("\n" <> (unUnionKind unionKind) <> "\n", mempty)
<> (toRawSql SELECT info recursiveQuery)
Expand Down
13 changes: 8 additions & 5 deletions src/Database/Esqueleto/Internal/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
import Data.Kind (Type)
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Monoid as Monoid

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.6)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Data.Monoid’ is redundant

Check warning on line 66 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Data.Monoid’ is redundant
import Data.Proxy (Proxy(..))
import Data.Set (Set)
import qualified Data.Set as Set
Expand All @@ -73,7 +73,7 @@
import Data.Typeable (Typeable)
import Database.Esqueleto.Internal.ExprParser (TableAccess(..), parseOnExpr)
import Database.Esqueleto.Internal.PersistentImport
import Database.Persist (EntityNameDB(..), FieldNameDB(..), SymbolToField(..))

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Persist’ is redundant

Check warning on line 76 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Persist’ is redundant
import qualified Database.Persist
import Database.Persist.Sql.Util
( entityColumnCount
Expand Down Expand Up @@ -1433,7 +1433,7 @@
unique = finalR uniqueConstructor
-- there must be a better way to get the constrain name from a unique, make this not a list search
filterF = (==) (persistUniqueToFieldNames unique) . uniqueFields
uniqueDef = head . filter filterF . getEntityUniques . entityDef $ proxy

Check warning on line 1436 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

In the use of ‘head’

Check warning on line 1436 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

In the use of ‘head’

Check warning on line 1436 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

In the use of ‘head’

Check warning on line 1436 in src/Database/Esqueleto/Internal/Internal.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

In the use of ‘head’

-- | Render updates to be use in a SET clause for a given sql backend.
--
Expand Down Expand Up @@ -1998,8 +1998,10 @@
| NormalCommonTableExpression
deriving Eq

data CommonTableExpressionClause =
CommonTableExpressionClause CommonTableExpressionKind Ident (IdentInfo -> (TLB.Builder, [PersistValue]))
type CommonTableExpressionModifierAfterAs = CommonTableExpressionClause -> IdentInfo -> TLB.Builder

data CommonTableExpressionClause
= CommonTableExpressionClause CommonTableExpressionKind CommonTableExpressionModifierAfterAs Ident (IdentInfo -> (TLB.Builder, [PersistValue]))

data SubQueryType
= NormalSubQuery
Expand Down Expand Up @@ -3212,14 +3214,15 @@
| hasRecursive = "WITH RECURSIVE "
| otherwise = "WITH "
where

hasRecursive =
elem RecursiveCommonTableExpression
$ fmap (\(CommonTableExpressionClause cteKind _ _) -> cteKind)
$ fmap (\(CommonTableExpressionClause cteKind _ _ _) -> cteKind)
$ cteClauses

cteClauseToText (CommonTableExpressionClause _ cteIdent cteFn) =
cteClauseToText clause@(CommonTableExpressionClause _ cteModifier cteIdent cteFn) =
first
(\tlb -> useIdent info cteIdent <> " AS " <> parens tlb)
(\tlb -> useIdent info cteIdent <> " AS " <> cteModifier clause info <> parens tlb)
(cteFn info)

cteBody =
Expand Down
89 changes: 87 additions & 2 deletions src/Database/Esqueleto/PostgreSQL.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
, forKeyShareOf
, filterWhere
, values
, withMaterialized
, withNotMaterialized
, ascNullsFirst
, ascNullsLast
, descNullsFirst
Expand All @@ -52,15 +54,22 @@
import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO(..))
import qualified Control.Monad.Trans.Reader as R
import qualified Control.Monad.Trans.Writer as W
import Data.Int (Int64)
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import Data.Proxy (Proxy(..))
import qualified Data.Text.Internal.Builder as TLB
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TLB

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.6)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant

Check warning on line 64 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Data.Text.Lazy.Builder’ is redundant
import Data.Time.Clock (UTCTime)
import qualified Database.Esqueleto.Experimental as Ex
import Database.Esqueleto.Internal.Internal hiding (random_)
import qualified Database.Esqueleto.Experimental.From as Ex

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.6)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant

Check warning on line 67 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The qualified import of ‘Database.Esqueleto.Experimental.From’ is redundant
import Database.Esqueleto.Experimental.From.CommonTableExpression

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.6)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant

Check warning on line 68 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Esqueleto.Experimental.From.CommonTableExpression’ is redundant
import Database.Esqueleto.Experimental.From.SqlSetOperation

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.6)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.8)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 8.10)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.2)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.0)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant

Check warning on line 69 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The import of ‘Database.Esqueleto.Experimental.From.SqlSetOperation’ is redundant
import Database.Esqueleto.Experimental.ToAlias
import Database.Esqueleto.Experimental.ToAliasReference
import Database.Esqueleto.Internal.Internal hiding (From(..), from, on, random_)
import Database.Esqueleto.Internal.PersistentImport hiding
(uniqueFields, upsert, upsertBy)
import Database.Persist.SqlBackend
Expand Down Expand Up @@ -244,7 +253,7 @@
queryVals =
addVals updateVals
xs <- rawSql queryText queryVals
pure (head xs)

Check warning on line 256 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

In the use of ‘head’

Check warning on line 256 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.8)

In the use of ‘head’

Check warning on line 256 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

In the use of ‘head’

Check warning on line 256 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.10)

In the use of ‘head’
#else
uDef = toUniqueDef uniqueKey
handler conn f = fmap head $ uncurry rawSql $
Expand Down Expand Up @@ -286,7 +295,7 @@
-- @since 3.1.3
insertSelectWithConflict
:: forall a m val backend
. (FinalResult a, KnowResult a ~ Unique val, MonadIO m, PersistEntity val, SqlBackendCanWrite backend)

Check warning on line 298 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The use of ‘~’ without TypeOperators

Check warning on line 298 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The use of ‘~’ without TypeOperators

Check warning on line 298 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The use of ‘~’ without TypeOperators

Check warning on line 298 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The use of ‘~’ without TypeOperators
=> a
-- ^ Unique constructor or a unique, this is used just to get the name of
-- the postgres constraint, the value(s) is(are) never used, so if you have
Expand All @@ -306,7 +315,7 @@
-- @since 3.1.3
insertSelectWithConflictCount
:: forall a val m backend
. (FinalResult a, KnowResult a ~ Unique val, MonadIO m, PersistEntity val,

Check warning on line 318 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The use of ‘~’ without TypeOperators

Check warning on line 318 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.4)

The use of ‘~’ without TypeOperators

Check warning on line 318 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The use of ‘~’ without TypeOperators

Check warning on line 318 in src/Database/Esqueleto/PostgreSQL.hs

View workflow job for this annotation

GitHub Actions / build (3.10.2.1, 9.6)

The use of ‘~’ without TypeOperators
SqlBackendCanWrite backend)
=> a
-> SqlQuery (SqlExpr (Insertion val))
Expand Down Expand Up @@ -490,7 +499,7 @@
forShareOf :: LockableEntity a => a -> OnLockedBehavior -> SqlQuery ()
forShareOf lockableEntities onLockedBehavior =
putLocking $ PostgresLockingClauses [PostgresLockingKind PostgresForShare (Just $ LockingOfClause lockableEntities) onLockedBehavior]

-- | `FOR KEY SHARE OF` syntax for postgres locking
-- allows locking of specific tables with a key share lock in a view or join
--
Expand All @@ -499,6 +508,82 @@
forKeyShareOf lockableEntities onLockedBehavior =
putLocking $ PostgresLockingClauses [PostgresLockingKind PostgresForKeyShare (Just $ LockingOfClause lockableEntities) onLockedBehavior]

-- | @WITH@ @MATERIALIZED@ clause is used to introduce a
-- [Common Table Expression (CTE)](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression)
-- with the MATERIALIZED keyword. The MATERIALIZED keyword is only supported in PostgreSQL >= version 12.
-- In Esqueleto, CTEs should be used as a subquery memoization tactic. PostgreSQL treats a materialized CTE as an optimization fence.
-- A materialized CTE is always fully calculated, and is not "inlined" with other table joins.
-- Without the MATERIALIZED keyword, PostgreSQL >= 12 may "inline" the CTE as though it was any other join.
-- You should always verify that using a materialized CTE will in fact improve your performance
-- over a regular subquery.
--
-- @
-- select $ do
-- cte <- withMaterialized subQuery
-- cteResult <- from cte
-- where_ $ cteResult ...
-- pure cteResult
-- @
--
--
-- For more information on materialized CTEs, see the PostgreSQL manual documentation on
-- [Common Table Expression Materialization](https://www.postgresql.org/docs/14/queries-with.html#id-1.5.6.12.7).
--
-- @since 3.5.14.0
withMaterialized :: ( ToAlias a
, ToAliasReference a
, SqlSelect a r
) => SqlQuery a -> SqlQuery (Ex.From a)
withMaterialized query = do
(ret, sideData) <- Q $ W.censor (\_ -> mempty) $ W.listen $ unQ query
aliasedValue <- toAlias ret
let aliasedQuery = Q $ W.WriterT $ pure (aliasedValue, sideData)
ident <- newIdentFor (DBName "cte")
let clause = CommonTableExpressionClause NormalCommonTableExpression (\_ _ -> "MATERIALIZED ") ident (\info -> toRawSql SELECT info aliasedQuery)
Q $ W.tell mempty{sdCteClause = [clause]}
ref <- toAliasReference ident aliasedValue
pure $ Ex.From $ pure (ref, (\_ info -> (useIdent info ident, mempty)))

-- | @WITH@ @NOT@ @MATERIALIZED@ clause is used to introduce a
-- [Common Table Expression (CTE)](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression)
-- with the NOT MATERIALIZED keywords. These are only supported in PostgreSQL >=
-- version 12. In Esqueleto, CTEs should be used as a subquery memoization
-- tactic. PostgreSQL treats a materialized CTE as an optimization fence. A
-- MATERIALIZED CTE is always fully calculated, and is not "inlined" with other
-- table joins. Sometimes, this is undesirable, so postgres provides the NOT
-- MATERIALIZED modifier to prevent this behavior, thus enabling it to possibly
-- decide to treat the CTE as any other join.
--
-- Given the above, it is unlikely that this function will be useful, as a
-- normal join should be used instead, but is provided for completeness.
--
-- @
-- select $ do
-- cte <- withNotMaterialized subQuery
-- cteResult <- from cte
-- where_ $ cteResult ...
-- pure cteResult
-- @
--
--
-- For more information on materialized CTEs, see the PostgreSQL manual documentation on
-- [Common Table Expression Materialization](https://www.postgresql.org/docs/14/queries-with.html#id-1.5.6.12.7).
--
-- @since 3.5.14.0
withNotMaterialized :: ( ToAlias a
, ToAliasReference a
, SqlSelect a r
) => SqlQuery a -> SqlQuery (Ex.From a)
withNotMaterialized query = do
(ret, sideData) <- Q $ W.censor (\_ -> mempty) $ W.listen $ unQ query
aliasedValue <- toAlias ret
let aliasedQuery = Q $ W.WriterT $ pure (aliasedValue, sideData)
ident <- newIdentFor (DBName "cte")
let clause = CommonTableExpressionClause NormalCommonTableExpression (\_ _ -> "NOT MATERIALIZED ") ident (\info -> toRawSql SELECT info aliasedQuery)
Q $ W.tell mempty{sdCteClause = [clause]}
ref <- toAliasReference ident aliasedValue
pure $ Ex.From $ pure (ref, (\_ info -> (useIdent info ident, mempty)))

-- | Ascending order of this field or SqlExpression with nulls coming first.
--
-- @since 3.5.14.0
Expand Down
7 changes: 5 additions & 2 deletions test/MySQL/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ import Database.Persist.MySQL
, connectPassword
, connectPort
, connectUser
, createMySQLPool
, defaultConnectInfo
, withMySQLConn
, createMySQLPool
)

import Test.Hspec

import Common.Test
import Data.Maybe (fromMaybe)
import System.Environment (lookupEnv)

testMysqlSum :: SpecDb
testMysqlSum = do
Expand Down Expand Up @@ -189,6 +191,7 @@ migrateIt = do
mkConnectionPool :: IO ConnectionPool
mkConnectionPool = do
ci <- isCI
mysqlHost <- (fromMaybe "localhost" <$> lookupEnv "MYSQL_HOST")
let connInfo
| ci =
defaultConnectInfo
Expand All @@ -200,7 +203,7 @@ mkConnectionPool = do
}
| otherwise =
defaultConnectInfo
{ connectHost = "localhost"
{ connectHost = mysqlHost
, connectUser = "travis"
, connectPassword = "esqutest"
, connectDatabase = "esqutest"
Expand Down
77 changes: 76 additions & 1 deletion test/PostgreSQL/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ import Database.Esqueleto hiding (random_)
import Database.Esqueleto.Experimental hiding (from, on, random_)
import qualified Database.Esqueleto.Experimental as Experimental
import qualified Database.Esqueleto.Internal.Internal as ES
import Database.Esqueleto.PostgreSQL (random_)
import Database.Esqueleto.PostgreSQL
(random_, withMaterialized, withNotMaterialized)
import qualified Database.Esqueleto.PostgreSQL as EP
import Database.Esqueleto.PostgreSQL.JSON hiding ((-.), (?.), (||.))
import qualified Database.Esqueleto.PostgreSQL.JSON as JSON
Expand Down Expand Up @@ -1232,6 +1233,80 @@ testCommonTableExpressions = do
pure res
asserting $ vals `shouldBe` fmap Value [2..11]

describe "MATERIALIZED CTEs" $ do
describe "withNotMaterialized" $ do
itDb "successfully executes query" $ do
void $ select $ do
limitedLordsCte <-
withNotMaterialized $ do
lords <- Experimental.from $ Experimental.table @Lord
limit 10
pure lords
lords <- Experimental.from limitedLordsCte
orderBy [asc $ lords ^. LordId]
pure lords

asserting noExceptions

itDb "generates the expected SQL" $ do
(sql, _) <- showQuery ES.SELECT $ do
limitedLordsCte <-
withNotMaterialized $ do
lords <- Experimental.from $ Experimental.table @Lord
limit 10
pure lords
lords <- Experimental.from limitedLordsCte
orderBy [asc $ lords ^. LordId]
pure lords

asserting $ sql `shouldBe` T.unlines
[ "WITH \"cte\" AS NOT MATERIALIZED (SELECT \"Lord\".\"county\" AS \"v_county\", \"Lord\".\"dogs\" AS \"v_dogs\""
, "FROM \"Lord\""
, " LIMIT 10"
, ")"
, "SELECT \"cte\".\"v_county\", \"cte\".\"v_dogs\""
, "FROM \"cte\""
, "ORDER BY \"cte\".\"v_county\" ASC"
]
asserting noExceptions


describe "withMaterialized" $ do
itDb "generates the expected SQL" $ do
(sql, _) <- showQuery ES.SELECT $ do
limitedLordsCte <-
withMaterialized $ do
lords <- Experimental.from $ Experimental.table @Lord
limit 10
pure lords
lords <- Experimental.from limitedLordsCte
orderBy [asc $ lords ^. LordId]
pure lords

asserting $ sql `shouldBe` T.unlines
[ "WITH \"cte\" AS MATERIALIZED (SELECT \"Lord\".\"county\" AS \"v_county\", \"Lord\".\"dogs\" AS \"v_dogs\""
, "FROM \"Lord\""
, " LIMIT 10"
, ")"
, "SELECT \"cte\".\"v_county\", \"cte\".\"v_dogs\""
, "FROM \"cte\""
, "ORDER BY \"cte\".\"v_county\" ASC"
]
asserting noExceptions

itDb "successfully executes query" $ do
void $ select $ do
limitedLordsCte <-
withMaterialized $ do
lords <- Experimental.from $ Experimental.table @Lord
limit 10
pure lords
lords <- Experimental.from limitedLordsCte
orderBy [asc $ lords ^. LordId]
pure lords

asserting noExceptions

testPostgresqlLocking :: SpecDb
testPostgresqlLocking = do
describe "Monoid instance" $ do
Expand Down
28 changes: 28 additions & 0 deletions test/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# docker-compose file for running postgres and mysql DBMS

# If using this to run the tests,
# while these containers are running (i.e. after something like)
# (cd test; docker-compose up -d)
# the tests must be told to use the hostname via MYSQL_HOST environment variable
# e.g. something like:
# MYSQL_HOST=127.0.0.1 stack test

version: '3'
services:
postgres:
image: 'postgres:15.2-alpine'
environment:
POSTGRES_USER: esqutest
POSTGRES_PASSWORD: esqutest
POSTGRES_DB: esqutest
ports:
- 5432:5432
mysql:
image: 'mysql:8.0.32'
environment:
MYSQL_USER: travis
MYSQL_PASSWORD: esqutest
MYSQL_ROOT_PASSWORD: esqutest
MYSQL_DATABASE: esqutest
ports:
- 3306:3306
Loading