{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Database.DuckDB.Simple.Copy (
CopyBindInfo (..),
CopyInitInfo (..),
CopySinkInfo (..),
CopyFinalizeInfo (..),
registerCopyToFunction,
) where
import Control.Exception (SomeException, bracket, displayException, onException, try)
import Control.Monad (forM, when)
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.FromField (Field (..))
import Database.DuckDB.Simple.Internal (Connection, destroyLogicalType, mkDeleteCallback, releaseStablePtrData, throwRegistrationError, withConnectionHandle)
import Database.DuckDB.Simple.Materialize (materializeValue)
import Foreign.C.String (CString, peekCString)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (Ptr, freeHaskellFunPtr, nullPtr)
import Foreign.StablePtr (StablePtr, castPtrToStablePtr, castStablePtrToPtr, deRefStablePtr, freeStablePtr, newStablePtr)
import Foreign.Storable (poke)
data CopyBindInfo = CopyBindInfo
{ CopyBindInfo -> [DuckDBType]
copyBindColumnTypes :: ![DuckDBType]
}
deriving (CopyBindInfo -> CopyBindInfo -> Bool
(CopyBindInfo -> CopyBindInfo -> Bool)
-> (CopyBindInfo -> CopyBindInfo -> Bool) -> Eq CopyBindInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CopyBindInfo -> CopyBindInfo -> Bool
== :: CopyBindInfo -> CopyBindInfo -> Bool
$c/= :: CopyBindInfo -> CopyBindInfo -> Bool
/= :: CopyBindInfo -> CopyBindInfo -> Bool
Eq, Int -> CopyBindInfo -> ShowS
[CopyBindInfo] -> ShowS
CopyBindInfo -> String
(Int -> CopyBindInfo -> ShowS)
-> (CopyBindInfo -> String)
-> ([CopyBindInfo] -> ShowS)
-> Show CopyBindInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CopyBindInfo -> ShowS
showsPrec :: Int -> CopyBindInfo -> ShowS
$cshow :: CopyBindInfo -> String
show :: CopyBindInfo -> String
$cshowList :: [CopyBindInfo] -> ShowS
showList :: [CopyBindInfo] -> ShowS
Show)
data CopyInitInfo bindState = CopyInitInfo
{ forall bindState. CopyInitInfo bindState -> bindState
copyInitBindState :: !bindState
, forall bindState. CopyInitInfo bindState -> String
copyInitFilePath :: !FilePath
}
data CopySinkInfo bindState globalState = CopySinkInfo
{ forall bindState globalState.
CopySinkInfo bindState globalState -> bindState
copySinkBindState :: !bindState
, forall bindState globalState.
CopySinkInfo bindState globalState -> globalState
copySinkGlobalState :: !globalState
}
data CopyFinalizeInfo bindState globalState = CopyFinalizeInfo
{ forall bindState globalState.
CopyFinalizeInfo bindState globalState -> bindState
copyFinalizeBindState :: !bindState
, forall bindState globalState.
CopyFinalizeInfo bindState globalState -> globalState
copyFinalizeGlobalState :: !globalState
}
data CopyFunctionResources = CopyFunctionResources
{ CopyFunctionResources -> DuckDBCopyFunctionBindFun
copyBindPtr :: !DuckDBCopyFunctionBindFun
, CopyFunctionResources -> DuckDBCopyFunctionGlobalInitFun
copyInitPtr :: !DuckDBCopyFunctionGlobalInitFun
, CopyFunctionResources -> DuckDBCopyFunctionSinkFun
copySinkPtr :: !DuckDBCopyFunctionSinkFun
, CopyFunctionResources -> DuckDBCopyFunctionFinalizeFun
copyFinalizePtr :: !DuckDBCopyFunctionFinalizeFun
, CopyFunctionResources -> DuckDBDeleteCallback
copyStateDestroyPtr :: !DuckDBDeleteCallback
}
registerCopyToFunction ::
forall bindState globalState.
Connection ->
Text ->
(CopyBindInfo -> IO bindState) ->
(CopyInitInfo bindState -> IO globalState) ->
(CopySinkInfo bindState globalState -> [[Field]] -> IO ()) ->
(CopyFinalizeInfo bindState globalState -> IO ()) ->
IO ()
registerCopyToFunction :: forall bindState globalState.
Connection
-> Text
-> (CopyBindInfo -> IO bindState)
-> (CopyInitInfo bindState -> IO globalState)
-> (CopySinkInfo bindState globalState -> [[Field]] -> IO ())
-> (CopyFinalizeInfo bindState globalState -> IO ())
-> IO ()
registerCopyToFunction Connection
conn Text
name CopyBindInfo -> IO bindState
bindFn CopyInitInfo bindState -> IO globalState
initFn CopySinkInfo bindState globalState -> [[Field]] -> IO ()
sinkFn CopyFinalizeInfo bindState globalState -> IO ()
finalizeFn = do
stateDestroyCb <- (Ptr () -> IO ()) -> IO DuckDBDeleteCallback
mkDeleteCallback Ptr () -> IO ()
releaseStablePtrData
bindPtr <- mkCopyBindFun (copyBindHandler stateDestroyCb bindFn)
initPtr <- mkCopyGlobalInitFun (copyGlobalInitHandler stateDestroyCb initFn)
sinkPtr <- mkCopySinkFun (copySinkHandler sinkFn)
finalizePtr <- mkCopyFinalizeFun (copyFinalizeHandler finalizeFn)
resources <-
newStablePtr
CopyFunctionResources
{ copyBindPtr = bindPtr
, copyInitPtr = initPtr
, copySinkPtr = sinkPtr
, copyFinalizePtr = finalizePtr
, copyStateDestroyPtr = stateDestroyCb
}
destroyCb <- mkDeleteCallback releaseCopyResources
let release =
DuckDBCopyFunctionBindFun -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBCopyFunctionBindFun
bindPtr
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DuckDBCopyFunctionGlobalInitFun -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBCopyFunctionGlobalInitFun
initPtr
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DuckDBCopyFunctionSinkFun -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBCopyFunctionSinkFun
sinkPtr
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DuckDBCopyFunctionFinalizeFun -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBCopyFunctionFinalizeFun
finalizePtr
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DuckDBDeleteCallback -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBDeleteCallback
stateDestroyCb
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> StablePtr CopyFunctionResources -> IO ()
forall a. StablePtr a -> IO ()
freeStablePtr StablePtr CopyFunctionResources
resources
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DuckDBDeleteCallback -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr DuckDBDeleteCallback
destroyCb
bracket c_duckdb_create_copy_function destroyCopyFunction \DuckDBCopyFunction
copyFun ->
(IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`onException` IO ()
release) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Text -> (CString -> IO ()) -> IO ()
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString Text
name \CString
cName ->
DuckDBCopyFunction -> CString -> IO ()
c_duckdb_copy_function_set_name DuckDBCopyFunction
copyFun CString
cName
DuckDBCopyFunction -> DuckDBCopyFunctionBindFun -> IO ()
c_duckdb_copy_function_set_bind DuckDBCopyFunction
copyFun DuckDBCopyFunctionBindFun
bindPtr
DuckDBCopyFunction -> DuckDBCopyFunctionGlobalInitFun -> IO ()
c_duckdb_copy_function_set_global_init DuckDBCopyFunction
copyFun DuckDBCopyFunctionGlobalInitFun
initPtr
DuckDBCopyFunction -> DuckDBCopyFunctionSinkFun -> IO ()
c_duckdb_copy_function_set_sink DuckDBCopyFunction
copyFun DuckDBCopyFunctionSinkFun
sinkPtr
DuckDBCopyFunction -> DuckDBCopyFunctionFinalizeFun -> IO ()
c_duckdb_copy_function_set_finalize DuckDBCopyFunction
copyFun DuckDBCopyFunctionFinalizeFun
finalizePtr
DuckDBCopyFunction -> Ptr () -> DuckDBDeleteCallback -> IO ()
c_duckdb_copy_function_set_extra_info DuckDBCopyFunction
copyFun (StablePtr CopyFunctionResources -> Ptr ()
forall a. StablePtr a -> Ptr ()
castStablePtrToPtr StablePtr CopyFunctionResources
resources) DuckDBDeleteCallback
destroyCb
Connection -> (DuckDBConnection -> IO ()) -> IO ()
forall a. Connection -> (DuckDBConnection -> IO a) -> IO a
withConnectionHandle Connection
conn \DuckDBConnection
connPtr -> do
rc <- DuckDBConnection -> DuckDBCopyFunction -> IO DuckDBState
c_duckdb_register_copy_function DuckDBConnection
connPtr DuckDBCopyFunction
copyFun
if rc == DuckDBSuccess
then pure ()
else throwRegistrationError "register copy function"
copyBindHandler ::
forall bindState.
DuckDBDeleteCallback ->
(CopyBindInfo -> IO bindState) ->
DuckDBCopyFunctionBindInfo ->
IO ()
copyBindHandler :: forall bindState.
DuckDBDeleteCallback
-> (CopyBindInfo -> IO bindState)
-> DuckDBCopyFunctionBindInfo
-> IO ()
copyBindHandler DuckDBDeleteCallback
destroyCb CopyBindInfo -> IO bindState
bindFn DuckDBCopyFunctionBindInfo
info = do
outcome <- IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try do
copyBindColumnTypes <- DuckDBCopyFunctionBindInfo -> IO [DuckDBType]
fetchColumnTypes DuckDBCopyFunctionBindInfo
info
bindState <- bindFn CopyBindInfo{copyBindColumnTypes}
stable <- newStablePtr bindState
c_duckdb_copy_function_bind_set_bind_data info (castStablePtrToPtr stable) destroyCb
reportCopyError c_duckdb_copy_function_bind_set_error info outcome
copyGlobalInitHandler ::
forall bindState globalState.
DuckDBDeleteCallback ->
(CopyInitInfo bindState -> IO globalState) ->
DuckDBCopyFunctionGlobalInitInfo ->
IO ()
copyGlobalInitHandler :: forall bindState globalState.
DuckDBDeleteCallback
-> (CopyInitInfo bindState -> IO globalState)
-> DuckDBCopyFunctionGlobalInitInfo
-> IO ()
copyGlobalInitHandler DuckDBDeleteCallback
destroyCb CopyInitInfo bindState -> IO globalState
initFn DuckDBCopyFunctionGlobalInitInfo
info = do
outcome <- IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try do
rawBindState <- DuckDBCopyFunctionGlobalInitInfo -> IO (Ptr ())
c_duckdb_copy_function_global_init_get_bind_data DuckDBCopyFunctionGlobalInitInfo
info
when (rawBindState == nullPtr) $
throwRegistrationError "missing copy bind state"
bindState <- deRefStablePtr (castPtrToStablePtr rawBindState :: StablePtr bindState)
pathPtr <- c_duckdb_copy_function_global_init_get_file_path info
filePath <-
if pathPtr == nullPtr
then pure ""
else peekCString pathPtr
globalState <- initFn CopyInitInfo{copyInitBindState = bindState, copyInitFilePath = filePath}
stable <- newStablePtr globalState
c_duckdb_copy_function_global_init_set_global_state info (castStablePtrToPtr stable) destroyCb
reportCopyError c_duckdb_copy_function_global_init_set_error info outcome
copySinkHandler ::
forall bindState globalState.
(CopySinkInfo bindState globalState -> [[Field]] -> IO ()) ->
DuckDBCopyFunctionSinkInfo ->
DuckDBDataChunk ->
IO ()
copySinkHandler :: forall bindState globalState.
(CopySinkInfo bindState globalState -> [[Field]] -> IO ())
-> DuckDBCopyFunctionSinkInfo -> DuckDBDataChunk -> IO ()
copySinkHandler CopySinkInfo bindState globalState -> [[Field]] -> IO ()
sinkFn DuckDBCopyFunctionSinkInfo
info DuckDBDataChunk
chunk = do
outcome <- IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try do
bindState <- (DuckDBCopyFunctionSinkInfo -> IO (Ptr ()))
-> DuckDBCopyFunctionSinkInfo -> IO bindState
forall a i. (i -> IO (Ptr ())) -> i -> IO a
readStablePtrState DuckDBCopyFunctionSinkInfo -> IO (Ptr ())
c_duckdb_copy_function_sink_get_bind_data DuckDBCopyFunctionSinkInfo
info
globalState <- readStablePtrState c_duckdb_copy_function_sink_get_global_state info
rows <- materializeChunkRows chunk
sinkFn CopySinkInfo{copySinkBindState = bindState, copySinkGlobalState = globalState} rows
reportCopyError c_duckdb_copy_function_sink_set_error info outcome
copyFinalizeHandler ::
forall bindState globalState.
(CopyFinalizeInfo bindState globalState -> IO ()) ->
DuckDBCopyFunctionFinalizeInfo ->
IO ()
copyFinalizeHandler :: forall bindState globalState.
(CopyFinalizeInfo bindState globalState -> IO ())
-> DuckDBCopyFunctionFinalizeInfo -> IO ()
copyFinalizeHandler CopyFinalizeInfo bindState globalState -> IO ()
finalizeFn DuckDBCopyFunctionFinalizeInfo
info = do
outcome <- IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try do
bindState <- (DuckDBCopyFunctionFinalizeInfo -> IO (Ptr ()))
-> DuckDBCopyFunctionFinalizeInfo -> IO bindState
forall a i. (i -> IO (Ptr ())) -> i -> IO a
readStablePtrState DuckDBCopyFunctionFinalizeInfo -> IO (Ptr ())
c_duckdb_copy_function_finalize_get_bind_data DuckDBCopyFunctionFinalizeInfo
info
globalState <- readStablePtrState c_duckdb_copy_function_finalize_get_global_state info
finalizeFn CopyFinalizeInfo{copyFinalizeBindState = bindState, copyFinalizeGlobalState = globalState}
reportCopyError c_duckdb_copy_function_finalize_set_error info outcome
reportCopyError :: (i -> CString -> IO ()) -> i -> Either SomeException () -> IO ()
reportCopyError :: forall i.
(i -> CString -> IO ()) -> i -> Either SomeException () -> IO ()
reportCopyError i -> CString -> IO ()
_ i
_ (Right ()) = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
reportCopyError i -> CString -> IO ()
setError i
info (Left SomeException
err) =
Text -> (CString -> IO ()) -> IO ()
forall a. Text -> (CString -> IO a) -> IO a
TextForeign.withCString (String -> Text
Text.pack (SomeException -> String
forall e. Exception e => e -> String
displayException SomeException
err)) \CString
cMsg ->
i -> CString -> IO ()
setError i
info CString
cMsg
fetchColumnTypes :: DuckDBCopyFunctionBindInfo -> IO [DuckDBType]
fetchColumnTypes :: DuckDBCopyFunctionBindInfo -> IO [DuckDBType]
fetchColumnTypes DuckDBCopyFunctionBindInfo
info = do
count <- DuckDBCopyFunctionBindInfo -> IO DuckDBIdx
c_duckdb_copy_function_bind_get_column_count DuckDBCopyFunctionBindInfo
info
let indices = [Int
0 .. DuckDBIdx -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DuckDBIdx
count Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1] :: [Int]
forM indices \Int
idx -> do
logical <- DuckDBCopyFunctionBindInfo -> DuckDBIdx -> IO DuckDBLogicalType
c_duckdb_copy_function_bind_get_column_type DuckDBCopyFunctionBindInfo
info (Int -> DuckDBIdx
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
idx)
dtype <- c_duckdb_get_type_id logical
destroyLogicalType logical
pure dtype
readStablePtrState :: forall a i. (i -> IO (Ptr ())) -> i -> IO a
readStablePtrState :: forall a i. (i -> IO (Ptr ())) -> i -> IO a
readStablePtrState i -> IO (Ptr ())
getter i
info = do
rawPtr <- i -> IO (Ptr ())
getter i
info
if rawPtr == nullPtr
then throwRegistrationError "missing copy callback state"
else deRefStablePtr (castPtrToStablePtr rawPtr :: StablePtr a)
materializeChunkRows :: DuckDBDataChunk -> IO [[Field]]
materializeChunkRows :: DuckDBDataChunk -> IO [[Field]]
materializeChunkRows DuckDBDataChunk
chunk = do
rawColumnCount <- DuckDBDataChunk -> IO DuckDBIdx
c_duckdb_data_chunk_get_column_count DuckDBDataChunk
chunk
let columnCount = DuckDBIdx -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DuckDBIdx
rawColumnCount :: Int
readers <- mapM (makeColumnReader chunk) [0 .. columnCount - 1]
rawRowCount <- c_duckdb_data_chunk_get_size chunk
let rowCount = DuckDBIdx -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DuckDBIdx
rawRowCount :: Int
forM [0 .. rowCount - 1] \Int
row ->
[ColumnReader] -> (ColumnReader -> IO Field) -> IO [Field]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [ColumnReader]
readers \ColumnReader
reader ->
ColumnReader
reader (Int -> DuckDBIdx
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
row)
type ColumnReader = DuckDBIdx -> IO Field
makeColumnReader :: DuckDBDataChunk -> Int -> IO ColumnReader
makeColumnReader :: DuckDBDataChunk -> Int -> IO ColumnReader
makeColumnReader DuckDBDataChunk
chunk Int
columnIndex = do
vector <- DuckDBDataChunk -> DuckDBIdx -> IO DuckDBVector
c_duckdb_data_chunk_get_vector DuckDBDataChunk
chunk (Int -> DuckDBIdx
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
columnIndex)
logical <- c_duckdb_vector_get_column_type vector
dtype <- c_duckdb_get_type_id logical
destroyLogicalType logical
dataPtr <- c_duckdb_vector_get_data vector
validity <- c_duckdb_vector_get_validity vector
let name = String -> Text
Text.pack (String
"column" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
columnIndex)
pure \DuckDBIdx
rowIdx -> do
fieldValue <- DuckDBType
-> DuckDBVector -> Ptr () -> Ptr DuckDBIdx -> Int -> IO FieldValue
materializeValue DuckDBType
dtype DuckDBVector
vector Ptr ()
dataPtr Ptr DuckDBIdx
validity (DuckDBIdx -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DuckDBIdx
rowIdx)
pure Field{fieldName = name, fieldIndex = columnIndex, fieldValue}
releaseCopyResources :: Ptr () -> IO ()
releaseCopyResources :: Ptr () -> IO ()
releaseCopyResources Ptr ()
rawPtr =
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Ptr ()
rawPtr Ptr () -> Ptr () -> Bool
forall a. Eq a => a -> a -> Bool
/= Ptr ()
forall a. Ptr a
nullPtr) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
let stablePtr :: StablePtr CopyFunctionResources
stablePtr = Ptr () -> StablePtr CopyFunctionResources
forall a. Ptr () -> StablePtr a
castPtrToStablePtr Ptr ()
rawPtr :: StablePtr CopyFunctionResources
CopyFunctionResources{copyBindPtr, copyInitPtr, copySinkPtr, copyFinalizePtr, copyStateDestroyPtr} <- StablePtr CopyFunctionResources -> IO CopyFunctionResources
forall a. StablePtr a -> IO a
deRefStablePtr StablePtr CopyFunctionResources
stablePtr
freeHaskellFunPtr copyBindPtr
freeHaskellFunPtr copyInitPtr
freeHaskellFunPtr copySinkPtr
freeHaskellFunPtr copyFinalizePtr
freeHaskellFunPtr copyStateDestroyPtr
freeStablePtr stablePtr
destroyCopyFunction :: DuckDBCopyFunction -> IO ()
destroyCopyFunction :: DuckDBCopyFunction -> IO ()
destroyCopyFunction DuckDBCopyFunction
copyFun =
(Ptr DuckDBCopyFunction -> IO ()) -> IO ()
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca \Ptr DuckDBCopyFunction
ptr -> Ptr DuckDBCopyFunction -> DuckDBCopyFunction -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr DuckDBCopyFunction
ptr DuckDBCopyFunction
copyFun 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 DuckDBCopyFunction -> IO ()
c_duckdb_destroy_copy_function Ptr DuckDBCopyFunction
ptr
foreign import ccall "wrapper"
mkCopyBindFun :: (DuckDBCopyFunctionBindInfo -> IO ()) -> IO DuckDBCopyFunctionBindFun
foreign import ccall "wrapper"
mkCopyGlobalInitFun :: (DuckDBCopyFunctionGlobalInitInfo -> IO ()) -> IO DuckDBCopyFunctionGlobalInitFun
foreign import ccall "wrapper"
mkCopySinkFun :: (DuckDBCopyFunctionSinkInfo -> DuckDBDataChunk -> IO ()) -> IO DuckDBCopyFunctionSinkFun
foreign import ccall "wrapper"
mkCopyFinalizeFun :: (DuckDBCopyFunctionFinalizeInfo -> IO ()) -> IO DuckDBCopyFunctionFinalizeFun