Skip to content

Commit c6f90a2

Browse files
committed
Integrate middleware changes with internalization of SqlBackend
1 parent 79725c1 commit c6f90a2

File tree

12 files changed

+132
-75
lines changed

12 files changed

+132
-75
lines changed

persistent-mysql/Database/Persist/MySQL.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ open' :: MySQL.ConnectInfo -> LogFunc -> IO SqlBackend
125125
open' ci logFunc = do
126126
conn <- MySQL.connect ci
127127
MySQLBase.autocommit conn False -- disable autocommit!
128-
smap <- makeSimpleStatementCache
128+
smap <- mkSimpleStatementCache
129129
return $
130130
setConnPutManySql putManySql $
131131
setConnRepsertManySql repsertManySql $
@@ -1242,7 +1242,7 @@ mockMigrate _connectInfo allDefs _getter val = do
12421242
-- the actual database isn't already present in the system.
12431243
mockMigration :: Migration -> IO ()
12441244
mockMigration mig = do
1245-
smap <- makeSimpleStatementCache
1245+
smap <- mkSimpleStatementCache
12461246
let sqlbackend =
12471247
mkSqlBackend MkSqlBackendArgs
12481248
{ connPrepare = \_ -> do

persistent-postgresql/Database/Persist/Postgresql.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import System.Environment (getEnvironment)
9090

9191
import Database.Persist.Sql
9292
import Database.Persist.SqlBackend
93+
import Database.Persist.SqlBackend.StatementCache
9394
import qualified Database.Persist.Sql.Util as Util
9495

9596
-- | A @libpq@ connection string. A simple example of connection
@@ -327,22 +328,22 @@ openSimpleConn = openSimpleConnWithVersion getServerVersion
327328
-- @since 2.9.1
328329
openSimpleConnWithVersion :: (PG.Connection -> IO (Maybe Double)) -> LogFunc -> PG.Connection -> IO SqlBackend
329330
openSimpleConnWithVersion getVerDouble logFunc conn = do
330-
smap <- makeSimpleStatementCache
331+
smap <- mkSimpleStatementCache
331332
serverVersion <- oldGetVersionToNew getVerDouble conn
332333
return $ createBackend logFunc serverVersion smap conn
333334

334335
-- | Create the backend given a logging function, server version, mutable statement cell,
335336
-- and connection.
336337
createBackend :: LogFunc -> NonEmpty Word
337-
-> StatementCache -> PG.Connection -> SqlBackend
338+
-> MkStatementCache -> PG.Connection -> SqlBackend
338339
createBackend logFunc serverVersion smap conn =
339340
maybe id setConnPutManySql (upsertFunction putManySql serverVersion) $
340341
maybe id setConnUpsertSql (upsertFunction upsertSql' serverVersion) $
341342
setConnInsertManySql insertManySql' $
342343
maybe id setConnRepsertManySql (upsertFunction repsertManySql serverVersion) $
343344
mkSqlBackend MkSqlBackendArgs
344345
{ connPrepare = prepare' conn
345-
, connStmtMap = smap
346+
, connStmtMap = mkStatementCache smap
346347
, connInsertSql = insertSql'
347348
, connClose = PG.close conn
348349
, connMigrateSql = migrate'
@@ -362,7 +363,6 @@ createBackend logFunc serverVersion smap conn =
362363
, connRDBMS = "postgresql"
363364
, connLimitOffset = decorateSQLWithLimitOffset "LIMIT ALL"
364365
, connLogFunc = logFunc
365-
, connStatementMiddleware = const pure
366366
}
367367

368368
prepare' :: PG.Connection -> Text -> IO Statement
@@ -1603,7 +1603,7 @@ data PostgresConfHooks = PostgresConfHooks
16031603
-- The default implementation does nothing.
16041604
--
16051605
-- @since 2.11.0
1606-
, pgConfHooksCreateStatementCache :: IO StatementCache
1606+
, pgConfHooksCreateStatementCache :: IO MkStatementCache
16071607
}
16081608

16091609
-- | Default settings for 'PostgresConfHooks'. See the individual fields of 'PostgresConfHooks' for the default values.
@@ -1613,7 +1613,7 @@ defaultPostgresConfHooks :: PostgresConfHooks
16131613
defaultPostgresConfHooks = PostgresConfHooks
16141614
{ pgConfHooksGetServerVersion = getServerVersionNonEmpty
16151615
, pgConfHooksAfterCreate = const $ pure ()
1616-
, pgConfHooksCreateStatementCache = makeSimpleStatementCache
1616+
, pgConfHooksCreateStatementCache = mkSimpleStatementCache
16171617
}
16181618

16191619

@@ -1695,7 +1695,7 @@ mockMigrate allDefs _ entity = fmap (fmap $ map showAlterDb) $ do
16951695
-- with the difference that an actual database is not needed.
16961696
mockMigration :: Migration -> IO ()
16971697
mockMigration mig = do
1698-
smap <- makeSimpleStatementCache
1698+
smap <- mkStatementCache <$> mkSimpleStatementCache
16991699
let sqlbackend =
17001700
mkSqlBackend MkSqlBackendArgs
17011701
{ connPrepare = \_ -> do
@@ -1719,7 +1719,6 @@ mockMigration mig = do
17191719
, connRDBMS = undefined
17201720
, connLimitOffset = undefined
17211721
, connLogFunc = undefined
1722-
, connStatementMiddleware = const pure
17231722
}
17241723
result = runReaderT $ runWriterT $ runWriterT mig
17251724
resp <- result sqlbackend

persistent-sqlite/Database/Persist/Sqlite.hs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ import qualified Data.Conduit.Combinators as C
7676
import qualified Data.Conduit.List as CL
7777
import qualified Data.HashMap.Lazy as HashMap
7878
import Data.Int (Int64)
79-
import Data.IORef
80-
import qualified Data.Map as Map
8179
import Data.Monoid ((<>))
8280
import Data.Pool (Pool)
8381
import Data.Text (Text)
@@ -91,6 +89,7 @@ import Database.Persist.Compatible
9189
#endif
9290
import Database.Persist.Sql
9391
import Database.Persist.SqlBackend
92+
import Database.Persist.SqlBackend.StatementCache
9493
import qualified Database.Persist.Sql.Util as Util
9594
import qualified Database.Sqlite as Sqlite
9695

@@ -267,7 +266,7 @@ wrapConnectionInfo connInfo conn logFunc = do
267266
Sqlite.reset conn stmt
268267
Sqlite.finalize stmt
269268

270-
smap <- makeSimpleStatementCache
269+
smap <- mkStatementCache <$> mkSimpleStatementCache
271270
return $
272271
setConnMaxParams 999 $
273272
setConnPutManySql putManySql $
@@ -288,7 +287,6 @@ wrapConnectionInfo connInfo conn logFunc = do
288287
, connRDBMS = "sqlite"
289288
, connLimitOffset = decorateSQLWithLimitOffset "LIMIT -1"
290289
, connLogFunc = logFunc
291-
, connStatementMiddleware = const pure
292290
}
293291
where
294292
helper t getter = do
@@ -455,7 +453,7 @@ migrate' allDefs getter val = do
455453
-- with the difference that an actual database isn't needed for it.
456454
mockMigration :: Migration -> IO ()
457455
mockMigration mig = do
458-
smap <- makeSimpleStatementCache
456+
smap <- mkStatementCache <$> mkSimpleStatementCache
459457
let sqlbackend =
460458
setConnMaxParams 999 $
461459
mkSqlBackend MkSqlBackendArgs
@@ -480,7 +478,6 @@ mockMigration mig = do
480478
, connRDBMS = "sqlite"
481479
, connLimitOffset = decorateSQLWithLimitOffset "LIMIT -1"
482480
, connLogFunc = undefined
483-
, connStatementMiddleware = const pure
484481
}
485482
result = runReaderT . runWriterT . runWriterT $ mig
486483
resp <- result sqlbackend

persistent/Database/Persist/Sql/Raw.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ import qualified Data.Text as T
1616
import Database.Persist
1717
import Database.Persist.Sql.Types
1818
import Database.Persist.Sql.Types.Internal
19-
import Database.Persist.SqlBackend.Internal
2019
import Database.Persist.Sql.Class
21-
import Database.Persist.Sql.Types.Internal (statementCacheLookup, StatementCache (statementCacheInsert))
20+
import Database.Persist.SqlBackend.Internal.StatementCache
2221

2322
rawQuery :: (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)
2423
=> Text
@@ -76,7 +75,8 @@ getStmt sql = do
7675

7776
getStmtConn :: SqlBackend -> Text -> IO Statement
7877
getStmtConn conn sql = do
79-
smap <- liftIO $ statementCacheLookup (connStmtMap conn) sql
78+
let cacheKey = mkCacheKeyFromQuery sql
79+
smap <- liftIO $ statementCacheLookup (connStmtMap conn) cacheKey
8080
case smap of
8181
Just stmt -> connStatementMiddleware conn sql stmt
8282
Nothing -> do
@@ -101,7 +101,7 @@ getStmtConn conn sql = do
101101
then stmtQuery stmt' x
102102
else liftIO $ throwIO $ StatementAlreadyFinalized sql
103103
}
104-
liftIO $ statementCacheInsert (connStmtMap conn) sql stmt
104+
liftIO $ statementCacheInsert (connStmtMap conn) cacheKey stmt
105105
connStatementMiddleware conn sql stmt
106106

107107
-- | Execute a raw SQL statement and return its results as a

persistent/Database/Persist/Sql/Run.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Database.Persist.Class.PersistStore
1717
import Database.Persist.Sql.Types
1818
import Database.Persist.Sql.Types.Internal
1919
import Database.Persist.Sql.Raw
20+
import Database.Persist.SqlBackend.Internal.StatementCache
2021

2122
-- | Get a connection from the pool, run the given action, and then return the
2223
-- connection to the pool.

persistent/Database/Persist/Sql/Types.hs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ module Database.Persist.Sql.Types
66
, SqlBackendCanRead, SqlBackendCanWrite, SqlReadT, SqlWriteT, IsSqlBackend
77
, OverflowNatural(..)
88
, ConnectionPoolConfig(..)
9-
, StatementCache(..)
10-
, makeSimpleStatementCache
119
) where
1210

1311
import Database.Persist.Types.Base (FieldCascade)

persistent/Database/Persist/SqlBackend.hs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Database.Persist.SqlBackend
1818
, setConnInsertManySql
1919
, setConnUpsertSql
2020
, setConnPutManySql
21+
, setConnStatementMiddleware
2122
) where
2223

2324
import Control.Monad.Reader
@@ -29,6 +30,7 @@ import qualified Database.Persist.SqlBackend.Internal as SqlBackend
2930
import Database.Persist.SqlBackend.Internal.MkSqlBackend as Mk (MkSqlBackendArgs(..))
3031
import Database.Persist.Types.Base
3132
import Database.Persist.SqlBackend.Internal.InsertSqlResult
33+
import Database.Persist.SqlBackend.Internal.Statement
3234
import Data.List.NonEmpty (NonEmpty)
3335

3436
-- $utilities
@@ -158,3 +160,16 @@ setConnPutManySql
158160
-> SqlBackend
159161
setConnPutManySql mkQuery sb =
160162
sb { connPutManySql = Just mkQuery }
163+
164+
-- | Set the 'connPutManySql field on the 'SqlBackend'. This can be used to
165+
-- locally alter the statement prior to the statement being queried or executed.
166+
-- If this is not set, it will have no effect.
167+
--
168+
-- @since 2.13.0.0
169+
setConnStatementMiddleware
170+
:: (Text -> Statement -> IO Statement)
171+
-> SqlBackend
172+
-> SqlBackend
173+
setConnStatementMiddleware middleware sb =
174+
sb { connStatementMiddleware = middleware }
175+

persistent/Database/Persist/SqlBackend/Internal.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Database.Persist.Types.Base
1515
import Data.Int
1616
import Data.IORef
1717
import Control.Monad.Reader
18+
import Database.Persist.SqlBackend.StatementCache
1819
import Database.Persist.SqlBackend.Internal.MkSqlBackend
1920
import Database.Persist.SqlBackend.Internal.Statement
2021
import Database.Persist.SqlBackend.Internal.InsertSqlResult
@@ -74,7 +75,7 @@ data SqlBackend = SqlBackend
7475
-- When left as 'Nothing', we default to using 'defaultPutMany'.
7576
--
7677
-- @since 2.8.1
77-
, connStmtMap :: IORef (Map Text Statement)
78+
, connStmtMap :: StatementCache
7879
-- ^ A reference to the cache of statements. 'Statement's are keyed by
7980
-- the 'Text' queries that generated them.
8081
, connClose :: IO ()
@@ -137,6 +138,9 @@ data SqlBackend = SqlBackend
137138
-- When left as 'Nothing', we default to using 'defaultRepsertMany'.
138139
--
139140
-- @since 2.9.0
141+
, connStatementMiddleware :: Text -> Statement -> IO Statement
142+
-- ^ Provide facilities for injecting middleware into statements
143+
-- to allow for instrumenting queries.
140144
}
141145

142146
-- | A function for creating a value of the 'SqlBackend' type. You should prefer
@@ -153,6 +157,7 @@ mkSqlBackend MkSqlBackendArgs {..} =
153157
, connPutManySql = Nothing
154158
, connUpsertSql = Nothing
155159
, connInsertManySql = Nothing
160+
, connStatementMiddleware = const pure
156161
, ..
157162
}
158163

persistent/Database/Persist/SqlBackend/Internal/MkSqlBackend.hs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import Data.Map (Map)
1313
import Data.String
1414
import Data.Text (Text)
1515
import Database.Persist.Class.PersistStore
16+
import Database.Persist.SqlBackend.StatementCache
1617
import Database.Persist.SqlBackend.Internal.Statement
17-
import Database.Persist.SqlBackend.Internal.StatementCache
1818
import Database.Persist.SqlBackend.Internal.InsertSqlResult
1919
import Database.Persist.SqlBackend.Internal.IsolationLevel
2020
import Database.Persist.Types.Base
@@ -35,7 +35,7 @@ data MkSqlBackendArgs = MkSqlBackendArgs
3535
, connInsertSql :: EntityDef -> [PersistValue] -> InsertSqlResult
3636
-- ^ This function generates the SQL and values necessary for
3737
-- performing an insert against the database.
38-
, connStmtMap :: InternalStatementCache
38+
, connStmtMap :: StatementCache
3939
-- ^ A reference to the cache of statements. 'Statement's are keyed by
4040
-- the 'Text' queries that generated them.
4141
, connClose :: IO ()
@@ -81,9 +81,6 @@ data MkSqlBackendArgs = MkSqlBackendArgs
8181
-- queries are the superior way to offer pagination.
8282
, connLogFunc :: LogFunc
8383
-- ^ A log function for the 'SqlBackend' to use.
84-
, connStatementMiddleware :: Text -> Statement -> IO Statement
85-
-- ^ Provide facilities for injecting middleware into statements
86-
-- to allow for instrumenting queries.
8784
}
8885

8986
type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,23 @@
1-
module Database.Persist.SqlBackend.Internal.StatementCache
2-
( StatementCache(..)
3-
, InternalStatementCache
4-
, makeSimpleStatementCache
5-
, internalizeStatementCache
6-
) where
1+
module Database.Persist.SqlBackend.Internal.StatementCache where
72

8-
import Data.Foldable
9-
import Data.IORef
10-
import qualified Data.Map as Map
113
import Data.Text (Text)
124
import Database.Persist.SqlBackend.Internal.Statement
135

14-
class StatementCache c where
15-
statementCacheLookup :: c -> Text -> IO (Maybe Statement)
16-
statementCacheInsert :: c -> Text -> Statement -> IO ()
17-
statementCacheClear :: c -> IO ()
18-
statementCacheSize :: c -> IO Int
19-
20-
data InternalStatementCache = InternalStatementCache
21-
{ _statementCacheLookup :: Text -> IO (Maybe Statement)
22-
, _statementCacheInsert :: Text -> Statement -> IO ()
23-
, _statementCacheClear :: IO ()
24-
, _statementCacheSize :: IO Int
6+
-- | A statement cache used to lookup statements that have already been prepared
7+
-- for a given query.
8+
--
9+
-- @since 2.13.0
10+
data StatementCache = StatementCache
11+
{ statementCacheLookup :: StatementCacheKey -> IO (Maybe Statement)
12+
, statementCacheInsert :: StatementCacheKey -> Statement -> IO ()
13+
, statementCacheClear :: IO ()
14+
, statementCacheSize :: IO Int
2515
}
2616

27-
instance StatementCache InternalStatementCache where
28-
statementCacheLookup = _statementCacheLookup
29-
statementCacheInsert = _statementCacheInsert
30-
statementCacheClear = _statementCacheClear
31-
statementCacheSize = _statementCacheSize
32-
33-
34-
internalizeStatementCache :: StatementCache c => c -> InternalStatementCache
35-
internalizeStatementCache c = InternalStatementCache
36-
{ _statementCacheLookup = statementCacheLookup c
37-
, _statementCacheInsert = statementCacheInsert c
38-
, _statementCacheClear = statementCacheClear c
39-
, _statementCacheSize = statementCacheSize c
40-
}
41-
42-
makeSimpleStatementCache :: IO InternalStatementCache
43-
makeSimpleStatementCache = do
44-
stmtMap <- newIORef Map.empty
45-
pure $ InternalStatementCache
46-
{ _statementCacheLookup = \sql -> Map.lookup sql <$> readIORef stmtMap
47-
, _statementCacheInsert = \sql stmt ->
48-
modifyIORef' stmtMap (Map.insert sql stmt)
49-
, _statementCacheClear = do
50-
oldStatements <- atomicModifyIORef' stmtMap (\oldStatements -> (Map.empty, oldStatements))
51-
traverse_ stmtFinalize oldStatements
52-
, _statementCacheSize = Map.size <$> readIORef stmtMap
53-
}
17+
newtype StatementCacheKey = StatementCacheKey { cacheKey :: Text }
18+
-- Wrapping around this to allow for more efficient keying mechanisms
19+
-- in the future, perhaps.
5420

21+
-- | Construct a `StatementCacheKey` from a raw SQL query.
22+
mkCacheKeyFromQuery :: Text -> StatementCacheKey
23+
mkCacheKeyFromQuery = StatementCacheKey

0 commit comments

Comments
 (0)