-
Notifications
You must be signed in to change notification settings - Fork 108
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
Support UPDATE/DELETE .. RETURNING #362
base: master
Are you sure you want to change the base?
Changes from 14 commits
d9777b4
48d71be
9788cbb
c97d455
c0044f7
b055b2e
63132c0
e9fa681
afee7b0
b8204ac
8ba4f3a
2e29743
7a75789
564cb83
1b93b6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,8 +1,5 @@ | ||||||||||||
{-# LANGUAGE CPP #-} | ||||||||||||
{-# LANGUAGE DataKinds #-} | ||||||||||||
{-# LANGUAGE TypeOperators #-} | ||||||||||||
{-# LANGUAGE TypeApplications #-} | ||||||||||||
{-# language DerivingStrategies, GeneralizedNewtypeDeriving #-} | ||||||||||||
|
||||||||||||
{-# LANGUAGE ConstraintKinds #-} | ||||||||||||
{-# LANGUAGE DeriveDataTypeable #-} | ||||||||||||
|
@@ -14,7 +11,6 @@ | |||||||||||
{-# LANGUAGE GADTs #-} | ||||||||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||||||||||||
{-# LANGUAGE InstanceSigs #-} | ||||||||||||
{-# LANGUAGE MultiParamTypeClasses #-} | ||||||||||||
{-# LANGUAGE OverloadedStrings #-} | ||||||||||||
{-# LANGUAGE Rank2Types #-} | ||||||||||||
{-# LANGUAGE ScopedTypeVariables #-} | ||||||||||||
|
@@ -60,7 +56,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 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.6.5)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.8.4)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.8.4)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.10.4)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.10.4)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.2.2)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.2.2)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.0.2)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.0.2)
Check warning on line 59 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.6.2)
|
||||||||||||
import Data.Proxy (Proxy(..)) | ||||||||||||
import Data.Set (Set) | ||||||||||||
import qualified Data.Set as Set | ||||||||||||
|
@@ -70,7 +66,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 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.8.4)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.8.4)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.10.4)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 8.10.4)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.2.2)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.2.2)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.0.2)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.0.2)
Check warning on line 69 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.6.2)
|
||||||||||||
import qualified Database.Persist | ||||||||||||
import Database.Persist.Sql.Util | ||||||||||||
( entityColumnCount | ||||||||||||
|
@@ -427,21 +423,23 @@ | |||||||||||
putLocking :: LockingClause -> SqlQuery () | ||||||||||||
putLocking clause = Q $ W.tell mempty { sdLockingClause = clause } | ||||||||||||
|
||||||||||||
{-# | ||||||||||||
DEPRECATED | ||||||||||||
sub_select | ||||||||||||
"sub_select \n \ | ||||||||||||
sub_select is an unsafe function to use. If used with a SqlQuery that \n \ | ||||||||||||
returns 0 results, then it may return NULL despite not mentioning Maybe \n \ | ||||||||||||
in the return type. If it returns more than 1 result, then it will throw a \n \ | ||||||||||||
SQL error.\n\n Instead, consider using one of the following alternatives: \n \ | ||||||||||||
- subSelect: attaches a LIMIT 1 and the Maybe return type, totally safe. \n \ | ||||||||||||
- subSelectMaybe: Attaches a LIMIT 1, useful for a query that already \n \ | ||||||||||||
has a Maybe in the return type. \n \ | ||||||||||||
- subSelectCount: Performs a count of the query - this is always safe. \n \ | ||||||||||||
- subSelectUnsafe: Performs no checks or guarantees. Safe to use with \n \ | ||||||||||||
countRows and friends." | ||||||||||||
#-} | ||||||||||||
-- | (Internal) Remember a @RETURNING@ clause in a query | ||||||||||||
tellReturning :: ReturningClause -> SqlQuery () | ||||||||||||
tellReturning clause = Q $ W.tell mempty { sdReturningClause = clause } | ||||||||||||
|
||||||||||||
{-# DEPRECATED sub_select | ||||||||||||
[ "sub_select is an unsafe function to use. If used with a SqlQuery that" | ||||||||||||
, "returns 0 results, then it may return NULL despite not mentioning Maybe" | ||||||||||||
, "in the return type. If it returns more than 1 result, then it will throw a" | ||||||||||||
, "SQL error.\n\n Instead, consider using one of the following alternatives:" | ||||||||||||
, "- subSelect: attaches a LIMIT 1 and the Maybe return type, totally safe. " | ||||||||||||
, "- subSelectMaybe: Attaches a LIMIT 1, useful for a query that already" | ||||||||||||
, " has a Maybe in the return type." | ||||||||||||
, "- subSelectCount: Performs a count of the query - this is always safe." | ||||||||||||
, "- subSelectUnsafe: Performs no checks or guarantees. Safe to use with" | ||||||||||||
, " countRows and friends." | ||||||||||||
] | ||||||||||||
#-} | ||||||||||||
-- | Execute a subquery @SELECT@ in an SqlExpression. Returns a | ||||||||||||
-- simple value so should be used only when the @SELECT@ query | ||||||||||||
-- is guaranteed to return just one row. | ||||||||||||
|
@@ -665,7 +663,7 @@ | |||||||||||
first (parensM p) . isNullExpr $ f Never info | ||||||||||||
where | ||||||||||||
isNullExpr :: (TLB.Builder, a) -> (TLB.Builder, a) | ||||||||||||
isNullExpr = first ((<> " IS NULL")) | ||||||||||||
isNullExpr = first (<> " IS NULL") | ||||||||||||
|
||||||||||||
-- | An alias for 'isNothing' that avoids clashing with the function from | ||||||||||||
-- "Data.Maybe" 'Data.Maybe.isNothing'. | ||||||||||||
|
@@ -1319,7 +1317,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 1320 in src/Database/Esqueleto/Internal/Internal.hs GitHub Actions / build (3.10.2.1, 9.8.1)
|
||||||||||||
|
||||||||||||
-- | Render updates to be use in a SET clause for a given sql backend. | ||||||||||||
-- | ||||||||||||
|
@@ -1835,14 +1833,15 @@ | |||||||||||
, sdLimitClause :: !LimitClause | ||||||||||||
, sdLockingClause :: !LockingClause | ||||||||||||
, sdCteClause :: ![CommonTableExpressionClause] | ||||||||||||
, sdReturningClause :: !ReturningClause | ||||||||||||
ulidtko marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
} | ||||||||||||
|
||||||||||||
instance Semigroup SideData where | ||||||||||||
SideData d f s w g h o l k c <> SideData d' f' s' w' g' h' o' l' k' c' = | ||||||||||||
SideData (d <> d') (f <> f') (s <> s') (w <> w') (g <> g') (h <> h') (o <> o') (l <> l') (k <> k') (c <> c') | ||||||||||||
SideData d f s w g h o l k c r <> SideData d' f' s' w' g' h' o' l' k' c' r' = | ||||||||||||
SideData (d <> d') (f <> f') (s <> s') (w <> w') (g <> g') (h <> h') (o <> o') (l <> l') (k <> k') (c <> c') (r <> r') | ||||||||||||
|
||||||||||||
instance Monoid SideData where | ||||||||||||
mempty = SideData mempty mempty mempty mempty mempty mempty mempty mempty mempty mempty | ||||||||||||
mempty = SideData mempty mempty mempty mempty mempty mempty mempty mempty mempty mempty mempty | ||||||||||||
mappend = (<>) | ||||||||||||
|
||||||||||||
-- | The @DISTINCT@ "clause". | ||||||||||||
|
@@ -1879,6 +1878,10 @@ | |||||||||||
data CommonTableExpressionClause = | ||||||||||||
CommonTableExpressionClause CommonTableExpressionKind Ident (IdentInfo -> (TLB.Builder, [PersistValue])) | ||||||||||||
|
||||||||||||
data ReturningClause | ||||||||||||
= ReturningNothing -- ^ The default, absent clause. | ||||||||||||
| ReturningStar -- ^ @RETURNING@ is present. | ||||||||||||
Comment on lines
+1881
to
+1883
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we use
Suggested change
But There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep you're right; I initially tried something different, this had more constructors — but it ended up isomorphic to Maybe (). Will refactor. |
||||||||||||
|
||||||||||||
data SubQueryType | ||||||||||||
= NormalSubQuery | ||||||||||||
| LateralSubQuery | ||||||||||||
|
@@ -2079,10 +2082,10 @@ | |||||||||||
mempty = GroupBy [] | ||||||||||||
mappend = (<>) | ||||||||||||
|
||||||||||||
-- | A @HAVING@ cause. | ||||||||||||
-- | A @HAVING@ clause. | ||||||||||||
type HavingClause = WhereClause | ||||||||||||
|
||||||||||||
-- | A @ORDER BY@ clause. | ||||||||||||
-- | An @ORDER BY@ clause. | ||||||||||||
type OrderByClause = SqlExpr OrderBy | ||||||||||||
|
||||||||||||
-- | A @LIMIT@ clause. | ||||||||||||
|
@@ -2117,6 +2120,15 @@ | |||||||||||
mempty = NoLockingClause | ||||||||||||
mappend = (<>) | ||||||||||||
|
||||||||||||
instance Semigroup ReturningClause where | ||||||||||||
(<>) ReturningNothing x = x | ||||||||||||
(<>) x ReturningNothing = x | ||||||||||||
(<>) ReturningStar ReturningStar = ReturningStar | ||||||||||||
|
||||||||||||
instance Monoid ReturningClause where | ||||||||||||
mempty = ReturningNothing | ||||||||||||
mappend = (<>) | ||||||||||||
|
||||||||||||
---------------------------------------------------------------------- | ||||||||||||
|
||||||||||||
-- | Identifier used for table names. | ||||||||||||
|
@@ -2400,6 +2412,42 @@ | |||||||||||
true :: SqlExpr (Value Bool) | ||||||||||||
true = val True | ||||||||||||
|
||||||||||||
-- | (Internal) The types which can appear in @RETURNING@ part of @UPDATE@ or @DELETE@ | ||||||||||||
-- | ||||||||||||
-- Many constructs appearing in @SELECT@ can go under @RETURNING@ -- but not all (e.g. | ||||||||||||
-- certainly not subqueries, @VALUES@ and such). Thus, this is a subclass of 'SqlSelect'. | ||||||||||||
-- | ||||||||||||
-- The fundeps duplicate those of 'SqlSelect' solely to provide somewhat more directly | ||||||||||||
-- understandable type errors. | ||||||||||||
class SqlSelect a r => InferReturning a r | r -> a, a -> r | ||||||||||||
instance PersistEntity ent => InferReturning (SqlExpr (Entity ent)) (Entity ent) | ||||||||||||
instance PersistEntity ent => InferReturning (SqlExpr (Maybe (Entity ent))) (Maybe (Entity ent)) | ||||||||||||
instance PersistField a => InferReturning (SqlExpr (Value a)) (Value a) | ||||||||||||
instance ( InferReturning a ra, InferReturning b rb) => InferReturning (a, b) (ra, rb) | ||||||||||||
instance ( InferReturning a ra | ||||||||||||
, InferReturning b rb | ||||||||||||
, InferReturning c rc | ||||||||||||
) => InferReturning (a, b, c) (ra, rb, rc) | ||||||||||||
instance ( InferReturning a ra | ||||||||||||
, InferReturning b rb | ||||||||||||
, InferReturning c rc | ||||||||||||
, InferReturning d rd | ||||||||||||
) => InferReturning (a, b, c, d) (ra, rb, rc, rd) | ||||||||||||
instance ( InferReturning a ra | ||||||||||||
, InferReturning b rb | ||||||||||||
, InferReturning c rc | ||||||||||||
, InferReturning d rd | ||||||||||||
, InferReturning e re | ||||||||||||
) => InferReturning (a, b, c, d, e) (ra, rb, rc, rd, re) | ||||||||||||
instance ( InferReturning a ra | ||||||||||||
ulidtko marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
, InferReturning b rb | ||||||||||||
, InferReturning c rc | ||||||||||||
, InferReturning d rd | ||||||||||||
, InferReturning e re | ||||||||||||
, InferReturning f rf | ||||||||||||
) => InferReturning (a, b, c, d, e, f) (ra, rb, rc, rd, re, rf) | ||||||||||||
-- tuple nesting provides unlimited arity if 6-tuple isn't enough | ||||||||||||
|
||||||||||||
-- | (Internal) Create a case statement. | ||||||||||||
-- | ||||||||||||
-- Since: 2.1.1 | ||||||||||||
|
@@ -2424,7 +2472,7 @@ | |||||||||||
in ( b0 <> " WHEN " <> b1 <> " THEN " <> b2, vals0 <> vals1 <> vals2 ) | ||||||||||||
|
||||||||||||
valueToSql :: SqlExpr (Value a) -> NeedParens -> IdentInfo -> (TLB.Builder, [PersistValue]) | ||||||||||||
valueToSql (ERaw _ f) p = f p | ||||||||||||
valueToSql (ERaw _ f) = f | ||||||||||||
|
||||||||||||
-- | (Internal) Create a custom binary operator. You /should/ | ||||||||||||
-- /not/ use this function directly since its type is very | ||||||||||||
|
@@ -2908,7 +2956,7 @@ | |||||||||||
:: (MonadIO m, SqlBackendCanWrite backend) | ||||||||||||
=> SqlQuery () | ||||||||||||
-> R.ReaderT backend m Int64 | ||||||||||||
deleteCount a = rawEsqueleto DELETE a | ||||||||||||
deleteCount = rawEsqueleto DELETE | ||||||||||||
|
||||||||||||
-- | Execute an @esqueleto@ @UPDATE@ query inside @persistent@'s | ||||||||||||
-- 'SqlPersistT' monad. Note that currently there are no type | ||||||||||||
|
@@ -2981,7 +3029,8 @@ | |||||||||||
orderByClauses | ||||||||||||
limitClause | ||||||||||||
lockingClause | ||||||||||||
cteClause = sd | ||||||||||||
cteClause | ||||||||||||
returningClause = sd | ||||||||||||
-- Pass the finalIdentState (containing all identifiers | ||||||||||||
-- that were used) to the subsequent calls. This ensures | ||||||||||||
-- that no name clashes will occur on subqueries that may | ||||||||||||
|
@@ -2999,6 +3048,7 @@ | |||||||||||
, makeOrderBy info orderByClauses | ||||||||||||
, makeLimit info limitClause | ||||||||||||
, makeLocking info lockingClause | ||||||||||||
, makeReturning info returningClause ret | ||||||||||||
] | ||||||||||||
|
||||||||||||
|
||||||||||||
|
@@ -3268,6 +3318,12 @@ | |||||||||||
plain v = (v,[]) | ||||||||||||
makeLocking _ NoLockingClause = mempty | ||||||||||||
|
||||||||||||
makeReturning :: SqlSelect a r | ||||||||||||
=> IdentInfo -> ReturningClause -> a -> (TLB.Builder, [PersistValue]) | ||||||||||||
makeReturning _ ReturningNothing _ = mempty | ||||||||||||
makeReturning info ReturningStar ret = ("RETURNING ", []) <> sqlSelectCols info ret | ||||||||||||
Comment on lines
+3329
to
+3332
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is getting the A typical -- | Not useful for 'select', but used for 'update' and 'delete'.
instance SqlSelect () () where
sqlSelectCols _ _ = ("1", [])
sqlSelectColCount _ = 1
sqlSelectProcessRow _ = Right ()
If this clause oops = select do
t <- from $ table @Foo
tellReturning ReturningStar
pure t Then we'll generate a query like: SELECT foo.a, foo.b, foo.c
FROM foo
RETURNING foo.a, foo.b, foo.c Which is definitely an error. So the possibility of error here makes me feel like exploring a different approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I thought about that, but -- | (Internal) Remember a @RETURNING@ clause in a query
tellReturning :: ReturningClause -> SqlQuery () The public interface is supposed to be only the added functions in Further, the rest of query builder logic didn't seem like it provided any sql correctness guarantees internally. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah,it's not a generally safe thing, but I do want to avoid adding more unsafety where possible. |
||||||||||||
|
||||||||||||
|
||||||||||||
parens :: TLB.Builder -> TLB.Builder | ||||||||||||
parens b = "(" <> (b <> ")") | ||||||||||||
|
||||||||||||
|
@@ -3937,14 +3993,14 @@ | |||||||||||
:: (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) | ||||||||||||
=> SqlQuery (SqlExpr (Insertion a)) | ||||||||||||
-> R.ReaderT backend m () | ||||||||||||
insertSelect a = void $ insertSelectCount a | ||||||||||||
insertSelect = void . insertSelectCount | ||||||||||||
|
||||||||||||
-- | Insert a 'PersistField' for every selected value, return the count afterward | ||||||||||||
insertSelectCount | ||||||||||||
:: (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) | ||||||||||||
=> SqlQuery (SqlExpr (Insertion a)) | ||||||||||||
-> R.ReaderT backend m Int64 | ||||||||||||
insertSelectCount a = rawEsqueleto INSERT_INTO a | ||||||||||||
insertSelectCount = rawEsqueleto INSERT_INTO | ||||||||||||
|
||||||||||||
-- | Renders an expression into 'Text'. Only useful for creating a textual | ||||||||||||
-- representation of the clauses passed to an "On" clause. | ||||||||||||
|
@@ -3954,7 +4010,7 @@ | |||||||||||
renderExpr sqlBackend e = case e of | ||||||||||||
ERaw _ mkBuilderValues -> | ||||||||||||
let (builder, _) = mkBuilderValues Never (sqlBackend, initialIdentState) | ||||||||||||
in (builderToText builder) | ||||||||||||
in builderToText builder | ||||||||||||
ulidtko marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
|
||||||||||||
-- | An exception thrown by 'RenderExpr' - it's not designed to handle composite | ||||||||||||
-- keys, and will blow up if you give it one. | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ | |
, chr | ||
, now_ | ||
, random_ | ||
, deleteReturning | ||
, updateReturning | ||
, upsert | ||
, upsertBy | ||
, insertSelectWithConflict | ||
|
@@ -41,6 +43,7 @@ | |
#if __GLASGOW_HASKELL__ < 804 | ||
import Data.Semigroup | ||
#endif | ||
import Conduit (withAcquire) | ||
import Control.Arrow (first) | ||
import Control.Exception (throw) | ||
import Control.Monad (void) | ||
|
@@ -75,7 +78,7 @@ | |
(PersistField a, PersistField [a]) | ||
=> SqlExpr (Value (Maybe [a])) | ||
-> SqlExpr (Value [a]) | ||
maybeArray x = coalesceDefault [x] (emptyArray) | ||
maybeArray x = coalesceDefault [x] emptyArray | ||
|
||
-- | Aggregate mode | ||
data AggMode | ||
|
@@ -238,7 +241,7 @@ | |
queryVals = | ||
addVals updateVals | ||
xs <- rawSql queryText queryVals | ||
pure (head xs) | ||
Check warning on line 244 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.8.1)
|
||
#else | ||
uDef = toUniqueDef uniqueKey | ||
handler conn f = fmap head $ uncurry rawSql $ | ||
|
@@ -280,7 +283,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 286 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 286 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 286 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 286 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 286 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.6.2)
|
||
=> 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 | ||
|
@@ -300,7 +303,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 306 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 306 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.4.5)
Check warning on line 306 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 306 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.8.1)
Check warning on line 306 in src/Database/Esqueleto/PostgreSQL.hs GitHub Actions / build (3.10.2.1, 9.6.2)
|
||
SqlBackendCanWrite backend) | ||
=> a | ||
-> SqlQuery (SqlExpr (Insertion val)) | ||
|
@@ -477,3 +480,49 @@ | |
forShareOf :: LockableEntity a => a -> OnLockedBehavior -> SqlQuery () | ||
forShareOf lockableEntities onLockedBehavior = | ||
putLocking $ PostgresLockingClauses [PostgresLockingKind PostgresForShare (Just $ LockingOfClause lockableEntities) onLockedBehavior] | ||
|
||
-- | `UPDATE .. RETURNING ..` SQL extension supported by Postgres. | ||
-- | ||
-- The instances of 'InferReturning' say what can be returned; currently includes | ||
-- whole entities, 'PersistField's, SQL expressions, tuples (possibly nested). | ||
-- | ||
-- Usage example: | ||
-- | ||
-- @ | ||
-- tuples <- updateReturning $ \p -> do | ||
-- set p [ PersonAge =. val (Just 0) ] | ||
-- where_ (isNothing $ p ^. PersonAge) | ||
-- return (val True, p ^. PersonName, (p ^. PersonFavNum) *. val 100) | ||
-- -- return p -- also works, returning (Entity Person) | ||
-- @ | ||
-- | ||
-- @since 3.5.11.3 | ||
updateReturning :: (MonadIO m, From from, InferReturning ex ret, SqlBackendCanWrite backend) | ||
=> (from -> SqlQuery ex) | ||
-> R.ReaderT backend m [ret] | ||
updateReturning block = do | ||
conn <- R.ask | ||
conduit <- rawSelectSource UPDATE (tellReturning ReturningStar >> from block) | ||
liftIO . withAcquire conduit $ flip R.runReaderT conn . runSource | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is the use of the new functionality - neat. This is where we say "Do the rawSelectSource UPDATE (tellReturning ReturningStar >> from block) So we modify the I think we can avoid modifying Consider If the If we look at the code for So, if we inline this and use updateReturning block = do
conn <- R.ask
let
query' = toRawSql UPDATE (conn, initialIdentState) block
((ret, _sd), _finalIdent) = flip S.runState -- ... (from toRawSql)
returningClause = ("RETURNING ", []) <> sqlSelectCols info ret
(queryWithReturning, vals) =
query' <> ("\n", []) <> returningClause
rawQuery queryWithReturning vals Now, it is unsatisfying that we have to run the query building twice with this - once in If conn <- R.ask
let
(ret, query') = toRawSqlWith UPDATE (conn, initialIdentState) block
returningClause = ("\nRETURNING ", []) <> sqlSelectCols info ret
(queryWithReturning, vals) =
query' <> returningClause
rawQuery queryWithReturning vals Then I think there's probably virtue in writing |
||
|
||
-- | `DELETE .. RETURNING` SQL extension supported by Postgres. | ||
-- | ||
-- The instances of 'InferReturning' say what can be returned; currently includes | ||
-- whole entities, 'PersistField's, SQL expressions, tuples (possibly nested). | ||
-- | ||
-- Usage example: | ||
-- | ||
-- @ | ||
-- removedRowsWithNames <- deleteReturning $ \p -> do | ||
-- where_ (isNothing $ p ^. PersonWeight) | ||
-- return (p, p ^. PersonName) | ||
-- @ | ||
-- | ||
-- @since 3.5.11.3 | ||
deleteReturning :: (MonadIO m, From from, InferReturning ex ret, SqlBackendCanWrite backend) | ||
=> (from -> SqlQuery ex) | ||
-> R.ReaderT backend m [ret] | ||
deleteReturning block = do | ||
conn <- R.ask | ||
conduit <- rawSelectSource DELETE (tellReturning ReturningStar >> from block) | ||
liftIO . withAcquire conduit $ flip R.runReaderT conn . runSource | ||
ulidtko marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation adds a field to the
SideData
type, which unfortunately does make this a breaking change and would need to go out as3.6.0.0
.However, I've suggested an alternative implementation that should be strictly additive, allowing it to be
3.5.12.0
.