Skip to content

Commit 09b6fad

Browse files
committed
use static function with SQLite to avoid dynamic wrapper
1 parent 48782e7 commit 09b6fad

File tree

3 files changed

+63
-8
lines changed

3 files changed

+63
-8
lines changed

simplexmq.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ library
218218
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20251009_queue_to_subscribe
219219
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20251010_client_notices
220220
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20251020_service_certs
221+
Simplex.Messaging.Agent.Store.SQLite.Util
221222
if flag(client_postgres) || flag(server_postgres)
222223
exposed-modules:
223224
Simplex.Messaging.Agent.Store.Postgres

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ module Simplex.Messaging.Agent.Store.SQLite
4242
)
4343
where
4444

45+
import Control.Concurrent.MVar
46+
import Control.Concurrent.STM
47+
import Control.Exception (bracketOnError, onException, throwIO)
4548
import Control.Monad
4649
import Data.Bits (xor)
4750
import Data.ByteArray (ScrubbedBytes)
@@ -55,21 +58,21 @@ import Data.Text (Text)
5558
import qualified Data.Text as T
5659
import Database.SQLite.Simple (Query (..))
5760
import qualified Database.SQLite.Simple as SQL
58-
import Database.SQLite.Simple.Function
5961
import Database.SQLite.Simple.QQ (sql)
6062
import qualified Database.SQLite3 as SQLite3
63+
import Database.SQLite3.Bindings
64+
import Foreign.C.Types
65+
import Foreign.Ptr
6166
import Simplex.Messaging.Agent.Store.Migrations (DBMigrate (..), sharedMigrateSchema)
6267
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
6368
import Simplex.Messaging.Agent.Store.SQLite.Common
6469
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
6570
import Simplex.Messaging.Agent.Store.Shared (Migration (..), MigrationConfig (..), MigrationError (..))
71+
import Simplex.Messaging.Agent.Store.SQLite.Util (SQLiteFunc, createStaticFunction, mkSQLiteFunc)
6672
import qualified Simplex.Messaging.Crypto as C
6773
import Simplex.Messaging.Util (ifM, safeDecodeUtf8)
6874
import System.Directory (copyFile, createDirectoryIfMissing, doesFileExist)
6975
import System.FilePath (takeDirectory, takeFileName, (</>))
70-
import UnliftIO.Exception (bracketOnError, onException, throwIO)
71-
import UnliftIO.MVar
72-
import UnliftIO.STM
7376

7477
-- * SQLite Store implementation
7578

@@ -114,19 +117,29 @@ connectDB path key track = do
114117
pure db
115118
where
116119
prepare db = do
117-
let exec = SQLite3.exec $ SQL.connectionHandle $ DB.conn db
118-
unless (BA.null key) . exec $ "PRAGMA key = " <> keyString key <> ";"
119-
exec . fromQuery $
120+
let db' = SQL.connectionHandle $ DB.conn db
121+
unless (BA.null key) . SQLite3.exec db' $ "PRAGMA key = " <> keyString key <> ";"
122+
SQLite3.exec db' . fromQuery $
120123
[sql|
121124
PRAGMA busy_timeout = 100;
122125
PRAGMA foreign_keys = ON;
123126
-- PRAGMA trusted_schema = OFF;
124127
PRAGMA secure_delete = ON;
125128
PRAGMA auto_vacuum = FULL;
126129
|]
127-
createFunction (DB.conn db) "simplex_xor_md5_combine" xorMd5Combine
130+
createStaticFunction db' "simplex_xor_md5_combine" 2 True sqliteXorMd5CombinePtr
128131
>>= either (throwIO . userError . show) pure
129132

133+
foreign export ccall "simplex_xor_md5_combine" sqliteXorMd5Combine :: SQLiteFunc
134+
135+
foreign import ccall "&simplex_xor_md5_combine" sqliteXorMd5CombinePtr :: FunPtr SQLiteFunc
136+
137+
sqliteXorMd5Combine :: SQLiteFunc
138+
sqliteXorMd5Combine = mkSQLiteFunc $ \cxt args -> do
139+
idsHash <- SQLite3.funcArgBlob args 0
140+
rId <- SQLite3.funcArgBlob args 1
141+
SQLite3.funcResultBlob cxt $ xorMd5Combine idsHash rId
142+
130143
xorMd5Combine :: ByteString -> ByteString -> ByteString
131144
xorMd5Combine idsHash rId = B.packZipWith xor idsHash $ C.md5Hash rId
132145

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
module Simplex.Messaging.Agent.Store.SQLite.Util where
2+
3+
import Control.Exception (SomeException, catch, mask_)
4+
import Data.ByteString (ByteString)
5+
import qualified Data.ByteString as B
6+
import Database.SQLite3.Direct (Database (..), FuncArgs (..), FuncContext (..))
7+
import Database.SQLite3.Bindings
8+
import Foreign.C.String
9+
import Foreign.Ptr
10+
import Foreign.StablePtr
11+
12+
data CFuncPtrs = CFuncPtrs (FunPtr CFunc) (FunPtr CFunc) (FunPtr CFuncFinal)
13+
14+
type SQLiteFunc = Ptr CContext -> CArgCount -> Ptr (Ptr CValue) -> IO ()
15+
16+
mkSQLiteFunc :: (FuncContext -> FuncArgs -> IO ()) -> SQLiteFunc
17+
mkSQLiteFunc f cxt nArgs cvals = catchAsResultError cxt $ f (FuncContext cxt) (FuncArgs nArgs cvals)
18+
{-# INLINE mkSQLiteFunc #-}
19+
20+
-- Based on createFunction from Database.SQLite3.Direct, but uses static function pointer to avoid dynamic wrapper that triggers DCL.
21+
createStaticFunction :: Database -> ByteString -> CArgCount -> Bool -> FunPtr SQLiteFunc -> IO (Either Error ())
22+
createStaticFunction (Database db) name nArgs isDet funPtr = mask_ $ do
23+
u <- newStablePtr $ CFuncPtrs funPtr nullFunPtr nullFunPtr
24+
let flags = if isDet then c_SQLITE_DETERMINISTIC else 0
25+
B.useAsCString name $ \namePtr ->
26+
toResult () <$> c_sqlite3_create_function_v2 db namePtr nArgs flags (castStablePtrToPtr u) funPtr nullFunPtr nullFunPtr nullFunPtr
27+
28+
-- Convert a 'CError' to a 'Either Error', in the common case where
29+
-- SQLITE_OK signals success and anything else signals an error.
30+
--
31+
-- Note that SQLITE_OK == 0.
32+
toResult :: a -> CError -> Either Error a
33+
toResult a (CError 0) = Right a
34+
toResult _ code = Left $ decodeError code
35+
36+
-- call c_sqlite3_result_error in the event of an error
37+
catchAsResultError :: Ptr CContext -> IO () -> IO ()
38+
catchAsResultError ctx action = catch action $ \exn -> do
39+
let msg = show (exn :: SomeException)
40+
withCAStringLen msg $ \(ptr, len) ->
41+
c_sqlite3_result_error ctx ptr (fromIntegral len)

0 commit comments

Comments
 (0)