cozo-hs-0.1.0.0: src/Database/Cozo/Internal.hs
{-# LANGUAGE StrictData #-}
{-# OPTIONS_GHC -Wno-typed-holes #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# OPTIONS_HADDOCK hide #-}
{-# HLINT ignore "Use newtype instead of data" #-}
module Database.Cozo.Internal (
open',
close',
runQuery',
importRelations',
exportRelations',
backup',
restore',
importFromBackup',
Connection,
InternalCozoError (..),
CozoNullResultPtrException (..),
) where
import Control.Exception (Exception)
import Control.Monad ((<=<))
import Data.ByteString (ByteString)
import Data.ByteString qualified as B
import Data.Coerce (coerce)
import Database.Cozo.Internal.Bindings (
cozoBackup,
cozoCloseDB,
cozoExportRelations,
cozoFreeStr,
cozoImportFromBackup,
cozoImportRelations,
cozoOpenDB,
cozoRestore,
cozoRunQuery,
)
import Foreign (
Ptr,
Storable (peek),
fromBool,
maybePeek,
new,
toBool,
)
import Foreign.C.Types (CChar, CInt)
import GHC.Generics (Generic)
newtype Connection = Connection (Ptr CInt)
{- |
Wrapper around primitive failure states.
-}
newtype InternalCozoError
= InternalCozoError ByteString
deriving (Show, Eq, Generic)
instance Exception InternalCozoError
data CozoNullResultPtrException = CozoNullResultPtrException deriving (Show, Eq, Generic)
instance Exception CozoNullResultPtrException
{- |
Open a connection to a cozo database
- engine: \"mem\", \"sqlite\" or \"rocksdb\"
- path: utf8 encoded filepath
- options: engine-specific options. \"{}\" is an acceptable empty value.
-}
open' ::
-- | engine: \"mem\", \"sqlite\" or \"rocksdb\"
ByteString ->
-- | path: utf8 encoded filepath
ByteString ->
-- | options: engine-specific options. \"{}\" is an acceptable emtpy value.
ByteString ->
IO (Either InternalCozoError Connection)
open' engineBs pathBs optionBs =
B.useAsCString engineBs $ \engine ->
B.useAsCString pathBs $ \path ->
B.useAsCString optionBs $ \options -> do
intPtr <- new @CInt 0
openMessagePtr <- cozoOpenDB engine path options intPtr
!mOpenMessage <- maybePeek B.packCString openMessagePtr
maybe
(pure . Right . Connection $ intPtr)
(\errStr -> (Left . InternalCozoError $ errStr) <$ cozoFreeStr openMessagePtr)
mOpenMessage
{- |
True if the database was closed and False if it was already closed or if it
does not exist.
-}
close' :: Connection -> IO Bool
close' = fmap toBool . cozoCloseDB <=< peek . coerce
{- |
Run a query.
The `CozoError` that might arise from this function is a `CozoNullResultPtr`.
If there are any errors internal to cozo, those will be returned as part of the
JSON string returned in the Right value. Simple returning a Right value does not
mean the query was successful.
- script: utf8 encoded script to execute
- params_raw: a utf8 encoded, JSON formatted map of parameters for use in the script.
-}
runQuery' ::
Connection ->
ByteString ->
ByteString ->
IO (Either CozoNullResultPtrException ByteString)
runQuery' c q p =
B.useAsCString q $ \q' ->
B.useAsCString p $ \p' ->
cozoCharPtrFn (\i -> cozoRunQuery i q' p' (fromBool False)) c
{- |
Import data in relations.
Triggers are not run for relations, if you wish to activate triggers, use a query
with parameters.
The given bytestring is a utf8, JSON formatted payload of relations.
In the same form as that given by `exportRelations'`
-}
importRelations' ::
Connection ->
ByteString ->
IO (Either CozoNullResultPtrException ByteString)
importRelations' c payloadBs =
B.useAsCString payloadBs $ \payload ->
cozoCharPtrFn (`cozoImportRelations` payload) c
{- |
Export relations into JSON
The given bytestring must be a utf8 encoded JSON payload. See the manual for expected
fields.
-}
exportRelations' ::
Connection ->
ByteString ->
IO (Either CozoNullResultPtrException ByteString)
exportRelations' c payloadBs =
B.useAsCString payloadBs $ \payload ->
cozoCharPtrFn (`cozoExportRelations` payload) c
{- |
Backup a database.
Accepts the path of the output file.
-}
backup' :: Connection -> ByteString -> IO (Either CozoNullResultPtrException ByteString)
backup' c pathBs =
B.useAsCString pathBs $ \path ->
cozoCharPtrFn (`cozoBackup` path) c
{- |
Restore a database from a backup.
-}
restore' :: Connection -> ByteString -> IO (Either CozoNullResultPtrException ByteString)
restore' c pathBs =
B.useAsCString pathBs $ \path ->
cozoCharPtrFn (`cozoRestore` path) c
{- |
Import relations from a backup.
Note that triggers are not run for the relations.
To run triggers, use a query with parameters.
- payload: @"{'path': ..., 'relations': [...]}"@
-}
importFromBackup' ::
Connection ->
ByteString ->
IO (Either CozoNullResultPtrException ByteString)
importFromBackup' c payloadBs =
B.useAsCString payloadBs $ \payload ->
cozoCharPtrFn (`cozoImportFromBackup` payload) c
{- |
Helper function for using cozo bindings that do an action and return a
string that needs to be freed.
-}
cozoCharPtrFn ::
(CInt -> IO (Ptr CChar)) ->
Connection ->
IO (Either CozoNullResultPtrException ByteString)
cozoCharPtrFn a (Connection intPtr) = do
dbId <- peek intPtr
rPtr <- a dbId
!mR <- maybePeek B.packCString rPtr
case mR of
Nothing -> pure . Left $ CozoNullResultPtrException
Just r -> Right r <$ cozoFreeStr rPtr