Merge pull request #133 from bitemyapp/matt/render-query
Render queries as Text
This commit is contained in:
commit
b4bfe538f9
@ -1,6 +1,9 @@
|
|||||||
Unreleased
|
Unreleased (3.1.1)
|
||||||
========
|
========
|
||||||
|
|
||||||
|
- @parsonsmatt
|
||||||
|
- [#133](https://github.com/bitemyapp/esqueleto/pull/133): Added `renderQueryToText` and related functions.
|
||||||
|
|
||||||
3.1.0
|
3.1.0
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,12 @@ module Database.Esqueleto
|
|||||||
, insertSelectCount
|
, insertSelectCount
|
||||||
, (<#)
|
, (<#)
|
||||||
, (<&>)
|
, (<&>)
|
||||||
|
-- ** Rendering Queries
|
||||||
|
, renderQueryToText
|
||||||
|
, renderQuerySelect
|
||||||
|
, renderQueryUpdate
|
||||||
|
, renderQueryDelete
|
||||||
|
, renderQueryInsertInto
|
||||||
-- * Internal.Language
|
-- * Internal.Language
|
||||||
, From
|
, From
|
||||||
-- * RDBMS-specific modules
|
-- * RDBMS-specific modules
|
||||||
|
|||||||
@ -1995,8 +1995,8 @@ builderToText = TL.toStrict . TLB.toLazyTextWith defaultChunkSize
|
|||||||
--
|
--
|
||||||
-- Note: if you're curious about the SQL query being generated by
|
-- Note: if you're curious about the SQL query being generated by
|
||||||
-- @esqueleto@, instead of manually using this function (which is
|
-- @esqueleto@, instead of manually using this function (which is
|
||||||
-- possible but tedious), you may just turn on query logging of
|
-- possible but tedious), see the 'renderQueryToText' function (along with
|
||||||
-- @persistent@.
|
-- 'renderQuerySelect', 'renderQueryUpdate', etc).
|
||||||
toRawSql
|
toRawSql
|
||||||
:: (SqlSelect a r, BackendCompatible SqlBackend backend)
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend)
|
||||||
=> Mode -> (backend, IdentState) -> SqlQuery a -> (TLB.Builder, [PersistValue])
|
=> Mode -> (backend, IdentState) -> SqlQuery a -> (TLB.Builder, [PersistValue])
|
||||||
@ -2032,6 +2032,90 @@ toRawSql mode (conn, firstIdentState) query =
|
|||||||
, makeLocking lockingClause
|
, makeLocking lockingClause
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Renders a 'SqlQuery' into a 'Text' value along with the list of
|
||||||
|
-- 'PersistValue's that would be supplied to the database for @?@ placeholders.
|
||||||
|
--
|
||||||
|
-- You must ensure that the 'Mode' you pass to this function corresponds with
|
||||||
|
-- the actual 'SqlQuery'. If you pass a query that uses incompatible features
|
||||||
|
-- (like an @INSERT@ statement with a @SELECT@ mode) then you'll get a weird
|
||||||
|
-- result.
|
||||||
|
--
|
||||||
|
-- @since 3.1.1
|
||||||
|
renderQueryToText
|
||||||
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
|
||||||
|
=> Mode
|
||||||
|
-- ^ Whether to render as an 'SELECT', 'DELETE', etc.
|
||||||
|
-> SqlQuery a
|
||||||
|
-- ^ The SQL query you want to render.
|
||||||
|
-> R.ReaderT backend m (T.Text, [PersistValue])
|
||||||
|
renderQueryToText mode query = do
|
||||||
|
backend <- R.ask
|
||||||
|
let (builder, pvals) = toRawSql mode (backend, initialIdentState) query
|
||||||
|
pure (builderToText builder, pvals)
|
||||||
|
|
||||||
|
-- | Renders a 'SqlQuery' into a 'Text' value along with the list of
|
||||||
|
-- 'PersistValue's that would be supplied to the database for @?@ placeholders.
|
||||||
|
--
|
||||||
|
-- You must ensure that the 'Mode' you pass to this function corresponds with
|
||||||
|
-- the actual 'SqlQuery'. If you pass a query that uses incompatible features
|
||||||
|
-- (like an @INSERT@ statement with a @SELECT@ mode) then you'll get a weird
|
||||||
|
-- result.
|
||||||
|
--
|
||||||
|
-- @since 3.1.1
|
||||||
|
renderQuerySelect
|
||||||
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
|
||||||
|
=> SqlQuery a
|
||||||
|
-- ^ The SQL query you want to render.
|
||||||
|
-> R.ReaderT backend m (T.Text, [PersistValue])
|
||||||
|
renderQuerySelect = renderQueryToText SELECT
|
||||||
|
|
||||||
|
-- | Renders a 'SqlQuery' into a 'Text' value along with the list of
|
||||||
|
-- 'PersistValue's that would be supplied to the database for @?@ placeholders.
|
||||||
|
--
|
||||||
|
-- You must ensure that the 'Mode' you pass to this function corresponds with
|
||||||
|
-- the actual 'SqlQuery'. If you pass a query that uses incompatible features
|
||||||
|
-- (like an @INSERT@ statement with a @SELECT@ mode) then you'll get a weird
|
||||||
|
-- result.
|
||||||
|
--
|
||||||
|
-- @since 3.1.1
|
||||||
|
renderQueryDelete
|
||||||
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
|
||||||
|
=> SqlQuery a
|
||||||
|
-- ^ The SQL query you want to render.
|
||||||
|
-> R.ReaderT backend m (T.Text, [PersistValue])
|
||||||
|
renderQueryDelete = renderQueryToText DELETE
|
||||||
|
|
||||||
|
-- | Renders a 'SqlQuery' into a 'Text' value along with the list of
|
||||||
|
-- 'PersistValue's that would be supplied to the database for @?@ placeholders.
|
||||||
|
--
|
||||||
|
-- You must ensure that the 'Mode' you pass to this function corresponds with
|
||||||
|
-- the actual 'SqlQuery'. If you pass a query that uses incompatible features
|
||||||
|
-- (like an @INSERT@ statement with a @SELECT@ mode) then you'll get a weird
|
||||||
|
-- result.
|
||||||
|
--
|
||||||
|
-- @since 3.1.1
|
||||||
|
renderQueryUpdate
|
||||||
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
|
||||||
|
=> SqlQuery a
|
||||||
|
-- ^ The SQL query you want to render.
|
||||||
|
-> R.ReaderT backend m (T.Text, [PersistValue])
|
||||||
|
renderQueryUpdate = renderQueryToText UPDATE
|
||||||
|
|
||||||
|
-- | Renders a 'SqlQuery' into a 'Text' value along with the list of
|
||||||
|
-- 'PersistValue's that would be supplied to the database for @?@ placeholders.
|
||||||
|
--
|
||||||
|
-- You must ensure that the 'Mode' you pass to this function corresponds with
|
||||||
|
-- the actual 'SqlQuery'. If you pass a query that uses incompatible features
|
||||||
|
-- (like an @INSERT@ statement with a @SELECT@ mode) then you'll get a weird
|
||||||
|
-- result.
|
||||||
|
--
|
||||||
|
-- @since 3.1.1
|
||||||
|
renderQueryInsertInto
|
||||||
|
:: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
|
||||||
|
=> SqlQuery a
|
||||||
|
-- ^ The SQL query you want to render.
|
||||||
|
-> R.ReaderT backend m (T.Text, [PersistValue])
|
||||||
|
renderQueryInsertInto = renderQueryToText INSERT_INTO
|
||||||
|
|
||||||
-- | (Internal) Mode of query being converted by 'toRawSql'.
|
-- | (Internal) Mode of query being converted by 'toRawSql'.
|
||||||
data Mode =
|
data Mode =
|
||||||
|
|||||||
@ -61,6 +61,11 @@ module Database.Esqueleto.Internal.Sql
|
|||||||
, veryUnsafeCoerceSqlExprValue
|
, veryUnsafeCoerceSqlExprValue
|
||||||
, veryUnsafeCoerceSqlExprValueList
|
, veryUnsafeCoerceSqlExprValueList
|
||||||
-- * Helper functions
|
-- * Helper functions
|
||||||
|
, renderQueryToText
|
||||||
|
, renderQuerySelect
|
||||||
|
, renderQueryUpdate
|
||||||
|
, renderQueryDelete
|
||||||
|
, renderQueryInsertInto
|
||||||
, makeOrderByNoNewline
|
, makeOrderByNoNewline
|
||||||
, uncommas'
|
, uncommas'
|
||||||
, parens
|
, parens
|
||||||
|
|||||||
@ -65,10 +65,12 @@ import Database.Persist.TH
|
|||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import UnliftIO
|
import UnliftIO
|
||||||
|
|
||||||
|
import Database.Persist (PersistValue(..))
|
||||||
import Data.Conduit (ConduitT, (.|), runConduit)
|
import Data.Conduit (ConduitT, (.|), runConduit)
|
||||||
import qualified Data.Conduit.List as CL
|
import qualified Data.Conduit.List as CL
|
||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Text.Lazy.Builder as TLB
|
import qualified Data.Text.Lazy.Builder as TLB
|
||||||
import qualified Data.Text.Internal.Lazy as TL
|
import qualified Data.Text.Internal.Lazy as TL
|
||||||
import qualified Database.Esqueleto.Internal.Sql as EI
|
import qualified Database.Esqueleto.Internal.Sql as EI
|
||||||
@ -1437,7 +1439,26 @@ testCountingRows run = do
|
|||||||
[Value n] <- select $ from $ return . countKind
|
[Value n] <- select $ from $ return . countKind
|
||||||
liftIO $ (n :: Int) `shouldBe` expected
|
liftIO $ (n :: Int) `shouldBe` expected
|
||||||
|
|
||||||
|
testRenderSql :: Run -> Spec
|
||||||
|
testRenderSql run =
|
||||||
|
describe "testRenderSql" $ do
|
||||||
|
it "works" $ do
|
||||||
|
(queryText, queryVals) <- run $ renderQuerySelect $
|
||||||
|
from $ \p -> do
|
||||||
|
where_ $ p ^. PersonName ==. val "Johhny Depp"
|
||||||
|
pure (p ^. PersonName, p ^. PersonAge)
|
||||||
|
-- the different backends use different quote marks, so I filter them out
|
||||||
|
-- here instead of making a duplicate test
|
||||||
|
Text.filter (\c -> c `notElem` ['`', '"']) queryText
|
||||||
|
`shouldBe`
|
||||||
|
Text.unlines
|
||||||
|
[ "SELECT Person.name, Person.age"
|
||||||
|
, "FROM Person"
|
||||||
|
, "WHERE Person.name = ?"
|
||||||
|
]
|
||||||
|
queryVals
|
||||||
|
`shouldBe`
|
||||||
|
[toPersistValue ("Johhny Depp" :: TL.Text)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1460,8 +1481,7 @@ tests run = do
|
|||||||
testMathFunctions run
|
testMathFunctions run
|
||||||
testCase run
|
testCase run
|
||||||
testCountingRows run
|
testCountingRows run
|
||||||
|
testRenderSql run
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
insert' :: ( Functor m
|
insert' :: ( Functor m
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user