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
|
||||
=======
|
||||
|
||||
|
||||
@ -86,6 +86,12 @@ module Database.Esqueleto
|
||||
, insertSelectCount
|
||||
, (<#)
|
||||
, (<&>)
|
||||
-- ** Rendering Queries
|
||||
, renderQueryToText
|
||||
, renderQuerySelect
|
||||
, renderQueryUpdate
|
||||
, renderQueryDelete
|
||||
, renderQueryInsertInto
|
||||
-- * Internal.Language
|
||||
, From
|
||||
-- * 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
|
||||
-- @esqueleto@, instead of manually using this function (which is
|
||||
-- possible but tedious), you may just turn on query logging of
|
||||
-- @persistent@.
|
||||
-- possible but tedious), see the 'renderQueryToText' function (along with
|
||||
-- 'renderQuerySelect', 'renderQueryUpdate', etc).
|
||||
toRawSql
|
||||
:: (SqlSelect a r, BackendCompatible SqlBackend backend)
|
||||
=> Mode -> (backend, IdentState) -> SqlQuery a -> (TLB.Builder, [PersistValue])
|
||||
@ -2032,6 +2032,90 @@ toRawSql mode (conn, firstIdentState) query =
|
||||
, 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'.
|
||||
data Mode =
|
||||
|
||||
@ -61,6 +61,11 @@ module Database.Esqueleto.Internal.Sql
|
||||
, veryUnsafeCoerceSqlExprValue
|
||||
, veryUnsafeCoerceSqlExprValueList
|
||||
-- * Helper functions
|
||||
, renderQueryToText
|
||||
, renderQuerySelect
|
||||
, renderQueryUpdate
|
||||
, renderQueryDelete
|
||||
, renderQueryInsertInto
|
||||
, makeOrderByNoNewline
|
||||
, uncommas'
|
||||
, parens
|
||||
|
||||
@ -65,10 +65,12 @@ import Database.Persist.TH
|
||||
import Test.Hspec
|
||||
import UnliftIO
|
||||
|
||||
import Database.Persist (PersistValue(..))
|
||||
import Data.Conduit (ConduitT, (.|), runConduit)
|
||||
import qualified Data.Conduit.List as CL
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Set as S
|
||||
import qualified Data.Text as Text
|
||||
import qualified Data.Text.Lazy.Builder as TLB
|
||||
import qualified Data.Text.Internal.Lazy as TL
|
||||
import qualified Database.Esqueleto.Internal.Sql as EI
|
||||
@ -1437,7 +1439,26 @@ testCountingRows run = do
|
||||
[Value n] <- select $ from $ return . countKind
|
||||
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
|
||||
testCase run
|
||||
testCountingRows run
|
||||
|
||||
|
||||
testRenderSql run
|
||||
|
||||
|
||||
insert' :: ( Functor m
|
||||
|
||||
Loading…
Reference in New Issue
Block a user