{-# LANGUAGE BlockArguments #-}

{- |
Module      : Database.DuckDB.Simple.Catalog
Description : High-level helpers for DuckDB catalog inspection.
-}
module Database.DuckDB.Simple.Catalog (
    CatalogEntry (..),
    catalogTypeName,
    lookupCatalogEntry,
) where

import Control.Exception (bracket)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Foreign as TextForeign
import Database.DuckDB.FFI
import Database.DuckDB.Simple.Internal (Connection, withClientContext)
import Foreign.C.String (CString, peekCString)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (nullPtr)
import Foreign.Storable (poke)

-- | A simplified view of a catalog entry returned by DuckDB.
data CatalogEntry = CatalogEntry
    { CatalogEntry -> Text
catalogEntryName :: !Text
    , CatalogEntry -> DuckDBCatalogEntryType
catalogEntryType :: !DuckDBCatalogEntryType
    }
    deriving (CatalogEntry -> CatalogEntry -> Bool
(CatalogEntry -> CatalogEntry -> Bool)
-> (CatalogEntry -> CatalogEntry -> Bool) -> Eq CatalogEntry
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CatalogEntry -> CatalogEntry -> Bool
== :: CatalogEntry -> CatalogEntry -> Bool
$c/= :: CatalogEntry -> CatalogEntry -> Bool
/= :: CatalogEntry -> CatalogEntry -> Bool
Eq, Int -> CatalogEntry -> ShowS
[CatalogEntry] -> ShowS
CatalogEntry -> String
(Int -> CatalogEntry -> ShowS)
-> (CatalogEntry -> String)
-> ([CatalogEntry] -> ShowS)
-> Show CatalogEntry
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CatalogEntry -> ShowS
showsPrec :: Int -> CatalogEntry -> ShowS
$cshow :: CatalogEntry -> String
show :: CatalogEntry -> String
$cshowList :: [CatalogEntry] -> ShowS
showList :: [CatalogEntry] -> ShowS
Show)

-- | Look up the backend type name of a named catalog.
catalogTypeName :: Connection -> Text -> IO (Maybe Text)
catalogTypeName :: Connection -> Text -> IO (Maybe Text)
catalogTypeName Connection
conn Text
catalogName =
    Connection
-> (DuckDBClientContext -> IO (Maybe Text)) -> IO (Maybe Text)
forall a. Connection -> (DuckDBClientContext -> IO a) -> IO a
withClientContext Connection
conn \DuckDBClientContext
ctx ->
        Text -> (CString -> IO (Maybe Text)) -> IO (Maybe Text)
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString Text
catalogName \CString
cName ->
            DuckDBClientContext
-> CString -> (DuckDBCatalog -> IO (Maybe Text)) -> IO (Maybe Text)
forall a.
DuckDBClientContext
-> CString -> (DuckDBCatalog -> IO (Maybe a)) -> IO (Maybe a)
withMaybeCatalog DuckDBClientContext
ctx CString
cName \DuckDBCatalog
catalog -> do
                namePtr <- DuckDBCatalog -> IO CString
c_duckdb_catalog_get_type_name DuckDBCatalog
catalog
                if namePtr == nullPtr
                    then pure Nothing
                    else Just . Text.pack <$> peekCString namePtr

-- | Look up a catalog entry by catalog, schema, name, and expected entry kind.
lookupCatalogEntry :: Connection -> Text -> Text -> Text -> DuckDBCatalogEntryType -> IO (Maybe CatalogEntry)
lookupCatalogEntry :: Connection
-> Text
-> Text
-> Text
-> DuckDBCatalogEntryType
-> IO (Maybe CatalogEntry)
lookupCatalogEntry Connection
conn Text
catalogName Text
schemaName Text
entryName DuckDBCatalogEntryType
entryType =
    Connection
-> (DuckDBClientContext -> IO (Maybe CatalogEntry))
-> IO (Maybe CatalogEntry)
forall a. Connection -> (DuckDBClientContext -> IO a) -> IO a
withClientContext Connection
conn \DuckDBClientContext
ctx ->
        Text
-> (CString -> IO (Maybe CatalogEntry)) -> IO (Maybe CatalogEntry)
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString Text
catalogName \CString
cCatalog ->
            Text
-> (CString -> IO (Maybe CatalogEntry)) -> IO (Maybe CatalogEntry)
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString Text
schemaName \CString
cSchema ->
                Text
-> (CString -> IO (Maybe CatalogEntry)) -> IO (Maybe CatalogEntry)
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString Text
entryName \CString
cEntry ->
                    DuckDBClientContext
-> CString
-> (DuckDBCatalog -> IO (Maybe CatalogEntry))
-> IO (Maybe CatalogEntry)
forall a.
DuckDBClientContext
-> CString -> (DuckDBCatalog -> IO (Maybe a)) -> IO (Maybe a)
withMaybeCatalog DuckDBClientContext
ctx CString
cCatalog \DuckDBCatalog
catalog ->
                        DuckDBCatalog
-> DuckDBClientContext
-> DuckDBCatalogEntryType
-> CString
-> CString
-> (DuckDBCatalogEntry -> IO (Maybe CatalogEntry))
-> IO (Maybe CatalogEntry)
forall a.
DuckDBCatalog
-> DuckDBClientContext
-> DuckDBCatalogEntryType
-> CString
-> CString
-> (DuckDBCatalogEntry -> IO (Maybe a))
-> IO (Maybe a)
withMaybeCatalogEntry DuckDBCatalog
catalog DuckDBClientContext
ctx DuckDBCatalogEntryType
entryType CString
cSchema CString
cEntry \DuckDBCatalogEntry
entry -> do
                            typ <- DuckDBCatalogEntry -> IO DuckDBCatalogEntryType
c_duckdb_catalog_entry_get_type DuckDBCatalogEntry
entry
                            namePtr <- c_duckdb_catalog_entry_get_name entry
                            if namePtr == nullPtr
                                then pure Nothing
                                else do
                                    name <- Text.pack <$> peekCString namePtr
                                    pure (Just CatalogEntry{catalogEntryName = name, catalogEntryType = typ})

destroyCatalog :: DuckDBCatalog -> IO ()
destroyCatalog :: DuckDBCatalog -> IO ()
destroyCatalog DuckDBCatalog
catalog =
    (Ptr DuckDBCatalog -> IO ()) -> IO ()
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca \Ptr DuckDBCatalog
ptr -> Ptr DuckDBCatalog -> DuckDBCatalog -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr DuckDBCatalog
ptr DuckDBCatalog
catalog IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr DuckDBCatalog -> IO ()
c_duckdb_destroy_catalog Ptr DuckDBCatalog
ptr

destroyCatalogEntry :: DuckDBCatalogEntry -> IO ()
destroyCatalogEntry :: DuckDBCatalogEntry -> IO ()
destroyCatalogEntry DuckDBCatalogEntry
entry =
    (Ptr DuckDBCatalogEntry -> IO ()) -> IO ()
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca \Ptr DuckDBCatalogEntry
ptr -> Ptr DuckDBCatalogEntry -> DuckDBCatalogEntry -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr DuckDBCatalogEntry
ptr DuckDBCatalogEntry
entry IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Ptr DuckDBCatalogEntry -> IO ()
c_duckdb_destroy_catalog_entry Ptr DuckDBCatalogEntry
ptr

withMaybeCatalog :: DuckDBClientContext -> CString -> (DuckDBCatalog -> IO (Maybe a)) -> IO (Maybe a)
withMaybeCatalog :: forall a.
DuckDBClientContext
-> CString -> (DuckDBCatalog -> IO (Maybe a)) -> IO (Maybe a)
withMaybeCatalog DuckDBClientContext
ctx CString
name DuckDBCatalog -> IO (Maybe a)
action = do
    catalog <- DuckDBClientContext -> CString -> IO DuckDBCatalog
c_duckdb_client_context_get_catalog DuckDBClientContext
ctx CString
name
    if catalog == nullPtr
        then pure Nothing
        else bracket (pure catalog) destroyCatalog action

withMaybeCatalogEntry ::
    DuckDBCatalog ->
    DuckDBClientContext ->
    DuckDBCatalogEntryType ->
    CString ->
    CString ->
    (DuckDBCatalogEntry -> IO (Maybe a)) ->
    IO (Maybe a)
withMaybeCatalogEntry :: forall a.
DuckDBCatalog
-> DuckDBClientContext
-> DuckDBCatalogEntryType
-> CString
-> CString
-> (DuckDBCatalogEntry -> IO (Maybe a))
-> IO (Maybe a)
withMaybeCatalogEntry DuckDBCatalog
catalog DuckDBClientContext
ctx DuckDBCatalogEntryType
entryType CString
schemaName CString
entryName DuckDBCatalogEntry -> IO (Maybe a)
action = do
    entry <- DuckDBCatalog
-> DuckDBClientContext
-> DuckDBCatalogEntryType
-> CString
-> CString
-> IO DuckDBCatalogEntry
c_duckdb_catalog_get_entry DuckDBCatalog
catalog DuckDBClientContext
ctx DuckDBCatalogEntryType
entryType CString
schemaName CString
entryName
    if entry == nullPtr
        then pure Nothing
        else bracket (pure entry) destroyCatalogEntry action