Skip to content

Commit 92a9579

Browse files
agent: option to add SQLite aggregates to DB connection (#1673)
* agent: add build_relations_vector function to sqlite * update aggregate * use static aggregate * remove relations --------- Co-authored-by: Evgeny Poberezkin <[email protected]>
1 parent cf9b7e5 commit 92a9579

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed

src/Simplex/Messaging/Agent/Store/SQLite.hs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ connectDB path functions key track = do
110110
pure db
111111
where
112112
prepare db = do
113-
let db' = SQL.connectionHandle $ DB.conn db
114113
unless (BA.null key) . SQLite3.exec db' $ "PRAGMA key = " <> keyString key <> ";"
115114
SQLite3.exec db' . fromQuery $
116115
[sql|
@@ -120,9 +119,13 @@ connectDB path functions key track = do
120119
PRAGMA secure_delete = ON;
121120
PRAGMA auto_vacuum = FULL;
122121
|]
123-
forM_ functions $ \SQLiteFuncDef {funcName, argCount, deterministic, funcPtr} ->
124-
createStaticFunction db' funcName argCount deterministic funcPtr
125-
>>= either (throwIO . userError . show) pure
122+
mapM_ addFunction functions
123+
where
124+
db' = SQL.connectionHandle $ DB.conn db
125+
addFunction SQLiteFuncDef {funcName, argCount, funcPtrs} =
126+
either (throwIO . userError . show) pure =<< case funcPtrs of
127+
SQLiteFuncPtr isDet funcPtr -> createStaticFunction db' funcName argCount isDet funcPtr
128+
SQLiteAggrPtrs stepPtr finalPtr -> createStaticAggregate db' funcName argCount stepPtr finalPtr
126129

127130
closeDBStore :: DBStore -> IO ()
128131
closeDBStore st@DBStore {dbClosed} =

src/Simplex/Messaging/Agent/Store/SQLite/Common.hs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Simplex.Messaging.Agent.Store.SQLite.Common
77
( DBStore (..),
88
DBOpts (..),
99
SQLiteFuncDef (..),
10+
SQLiteFuncPtrs (..),
1011
withConnection,
1112
withConnection',
1213
withTransaction,
@@ -55,14 +56,18 @@ data DBOpts = DBOpts
5556
track :: DB.TrackQueries
5657
}
5758

58-
-- e.g. `SQLiteFuncDef "name" 2 True f`
59+
-- e.g. `SQLiteFuncDef "func_name" 2 (SQLiteFuncPtr True func)`
60+
-- or `SQLiteFuncDef "aggr_name" 3 (SQLiteAggrPtrs step final)`
5961
data SQLiteFuncDef = SQLiteFuncDef
6062
{ funcName :: ByteString,
6163
argCount :: CArgCount,
62-
deterministic :: Bool,
63-
funcPtr :: FunPtr SQLiteFunc
64+
funcPtrs :: SQLiteFuncPtrs
6465
}
6566

67+
data SQLiteFuncPtrs
68+
= SQLiteFuncPtr {deterministic :: Bool, funcPtr :: FunPtr SQLiteFunc}
69+
| SQLiteAggrPtrs {stepPtr :: FunPtr SQLiteFunc, finalPtr :: FunPtr SQLiteFuncFinal}
70+
6671
withConnectionPriority :: DBStore -> Bool -> (DB.Connection -> IO a) -> IO a
6772
withConnectionPriority DBStore {dbSem, dbConnection} priority action
6873
| priority = E.bracket_ signal release $ withMVar dbConnection action

src/Simplex/Messaging/Agent/Store/SQLite/Util.hs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ module Simplex.Messaging.Agent.Store.SQLite.Util where
33
import Control.Exception (SomeException, catch, mask_)
44
import Data.ByteString (ByteString)
55
import qualified Data.ByteString as B
6+
import Data.IORef
67
import Database.SQLite3.Direct (Database (..), FuncArgs (..), FuncContext (..))
78
import Database.SQLite3.Bindings
89
import Foreign.C.String
910
import Foreign.Ptr
1011
import Foreign.StablePtr
12+
import Foreign.Storable
1113

1214
data CFuncPtrs = CFuncPtrs (FunPtr CFunc) (FunPtr CFunc) (FunPtr CFuncFinal)
1315

1416
type SQLiteFunc = Ptr CContext -> CArgCount -> Ptr (Ptr CValue) -> IO ()
1517

18+
type SQLiteFuncFinal = Ptr CContext -> IO ()
19+
1620
mkSQLiteFunc :: (FuncContext -> FuncArgs -> IO ()) -> SQLiteFunc
1721
mkSQLiteFunc f cxt nArgs cvals = catchAsResultError cxt $ f (FuncContext cxt) (FuncArgs nArgs cvals)
1822
{-# INLINE mkSQLiteFunc #-}
@@ -25,6 +29,50 @@ createStaticFunction (Database db) name nArgs isDet funPtr = mask_ $ do
2529
B.useAsCString name $ \namePtr ->
2630
toResult () <$> c_sqlite3_create_function_v2 db namePtr nArgs flags (castStablePtrToPtr u) funPtr nullFunPtr nullFunPtr nullFunPtr
2731

32+
mkSQLiteAggStep :: a -> (FuncContext -> FuncArgs -> a -> IO a) -> SQLiteFunc
33+
mkSQLiteAggStep initSt xStep cxt nArgs cvals = catchAsResultError cxt $ do
34+
-- we store the aggregate state in the buffer returned by
35+
-- c_sqlite3_aggregate_context as a StablePtr pointing to an IORef that
36+
-- contains the actual aggregate state
37+
aggCtx <- getAggregateContext cxt
38+
aggStPtr <- peek aggCtx
39+
aggStRef <-
40+
if castStablePtrToPtr aggStPtr /= nullPtr
41+
then deRefStablePtr aggStPtr
42+
else do
43+
aggStRef <- newIORef initSt
44+
aggStPtr' <- newStablePtr aggStRef
45+
poke aggCtx aggStPtr'
46+
return aggStRef
47+
aggSt <- readIORef aggStRef
48+
aggSt' <- xStep (FuncContext cxt) (FuncArgs nArgs cvals) aggSt
49+
writeIORef aggStRef aggSt'
50+
51+
mkSQLiteAggFinal :: a -> (FuncContext -> a -> IO ()) -> SQLiteFuncFinal
52+
mkSQLiteAggFinal initSt xFinal cxt = do
53+
aggCtx <- getAggregateContext cxt
54+
aggStPtr <- peek aggCtx
55+
if castStablePtrToPtr aggStPtr == nullPtr
56+
then catchAsResultError cxt $ xFinal (FuncContext cxt) initSt
57+
else do
58+
catchAsResultError cxt $ do
59+
aggStRef <- deRefStablePtr aggStPtr
60+
aggSt <- readIORef aggStRef
61+
xFinal (FuncContext cxt) aggSt
62+
freeStablePtr aggStPtr
63+
64+
getAggregateContext :: Ptr CContext -> IO (Ptr a)
65+
getAggregateContext cxt = c_sqlite3_aggregate_context cxt stPtrSize
66+
where
67+
stPtrSize = fromIntegral $ sizeOf (undefined :: StablePtr ())
68+
69+
-- Based on createAggregate from Database.SQLite3.Direct, but uses static function pointers to avoid dynamic wrappers that trigger DCL.
70+
createStaticAggregate :: Database -> ByteString -> CArgCount -> FunPtr SQLiteFunc -> FunPtr SQLiteFuncFinal -> IO (Either Error ())
71+
createStaticAggregate (Database db) name nArgs stepPtr finalPtr = mask_ $ do
72+
u <- newStablePtr $ CFuncPtrs nullFunPtr stepPtr finalPtr
73+
B.useAsCString name $ \namePtr ->
74+
toResult () <$> c_sqlite3_create_function_v2 db namePtr nArgs 0 (castStablePtrToPtr u) nullFunPtr stepPtr finalPtr nullFunPtr
75+
2876
-- Convert a 'CError' to a 'Either Error', in the common case where
2977
-- SQLITE_OK signals success and anything else signals an error.
3078
--

0 commit comments

Comments
 (0)