{-# LANGUAGE ScopedTypeVariables, FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} module Database.SQLite.Simple.Function ( Function , createFunction , deleteFunction ) where import Control.Exception import Data.Proxy import Database.SQLite3 as Base hiding (createFunction,deleteFunction,funcArgText,funcResultText) import qualified Database.SQLite3.Direct as Base import Database.SQLite.Simple import Database.SQLite.Simple.Internal (Field(..)) import Database.SQLite.Simple.ToField import Database.SQLite.Simple.FromField import Database.SQLite.Simple.Ok import qualified Data.Text as T import qualified Data.Text.Encoding as TE class Function a where argCount :: Proxy a -> Int deterministicFn :: Proxy a -> Bool evalFunction :: Base.FuncContext -> Base.FuncArgs -> Int -> a -> IO () instance {-# OVERLAPPING #-} (ToField a) => Function a where argCount :: Proxy a -> Int argCount = Int -> Proxy a -> Int forall a b. a -> b -> a const Int 0 deterministicFn :: Proxy a -> Bool deterministicFn = Bool -> Proxy a -> Bool forall a b. a -> b -> a const Bool True evalFunction :: FuncContext -> FuncArgs -> Int -> a -> IO () evalFunction FuncContext ctx FuncArgs _ Int _ a a = case a -> SQLData forall a. ToField a => a -> SQLData toField a a of SQLInteger Int64 r -> FuncContext -> Int64 -> IO () Base.funcResultInt64 FuncContext ctx Int64 r SQLFloat Double r -> FuncContext -> Double -> IO () Base.funcResultDouble FuncContext ctx Double r SQLText Text r -> FuncContext -> Utf8 -> IO () Base.funcResultText FuncContext ctx (Utf8 -> IO ()) -> Utf8 -> IO () forall a b. (a -> b) -> a -> b $ ByteString -> Utf8 Base.Utf8 (ByteString -> Utf8) -> ByteString -> Utf8 forall a b. (a -> b) -> a -> b $ Text -> ByteString TE.encodeUtf8 Text r SQLBlob ByteString r -> FuncContext -> ByteString -> IO () Base.funcResultBlob FuncContext ctx ByteString r SQLData SQLNull -> FuncContext -> IO () Base.funcResultNull FuncContext ctx instance {-# Overlapping #-} (Function a) => Function (IO a) where argCount :: Proxy (IO a) -> Int argCount = Int -> Proxy (IO a) -> Int forall a b. a -> b -> a const Int 0 deterministicFn :: Proxy (IO a) -> Bool deterministicFn = Bool -> Proxy (IO a) -> Bool forall a b. a -> b -> a const Bool False evalFunction :: FuncContext -> FuncArgs -> Int -> IO a -> IO () evalFunction FuncContext ctx FuncArgs args Int ca IO a a = IO a a IO a -> (a -> IO ()) -> IO () forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= FuncContext -> FuncArgs -> Int -> a -> IO () forall a. Function a => FuncContext -> FuncArgs -> Int -> a -> IO () evalFunction FuncContext ctx FuncArgs args Int ca instance {-# Overlapping #-} forall f r . (Function r, FromField f) => Function (f -> r) where argCount :: Proxy (f -> r) -> Int argCount = Int -> Proxy (f -> r) -> Int forall a b. a -> b -> a const (Int -> Proxy (f -> r) -> Int) -> Int -> Proxy (f -> r) -> Int forall a b. (a -> b) -> a -> b $ Proxy r -> Int forall a. Function a => Proxy a -> Int argCount (Proxy r forall k (t :: k). Proxy t Proxy :: Proxy r) Int -> Int -> Int forall a. Num a => a -> a -> a + Int 1 deterministicFn :: Proxy (f -> r) -> Bool deterministicFn = Bool -> Proxy (f -> r) -> Bool forall a b. a -> b -> a const (Bool -> Proxy (f -> r) -> Bool) -> Bool -> Proxy (f -> r) -> Bool forall a b. (a -> b) -> a -> b $ Proxy r -> Bool forall a. Function a => Proxy a -> Bool deterministicFn (Proxy r forall k (t :: k). Proxy t Proxy :: Proxy r) evalFunction :: FuncContext -> FuncArgs -> Int -> (f -> r) -> IO () evalFunction FuncContext ctx FuncArgs args Int ca f -> r fn = let ca' :: ArgCount ca' = Int -> ArgCount Base.ArgCount Int ca in do SQLData sqlv <- FuncArgs -> ArgCount -> IO ColumnType Base.funcArgType FuncArgs args ArgCount ca' IO ColumnType -> (ColumnType -> IO SQLData) -> IO SQLData forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= \ColumnType ct -> case ColumnType ct of ColumnType Base.IntegerColumn -> Int64 -> SQLData SQLInteger (Int64 -> SQLData) -> IO Int64 -> IO SQLData forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b <$> FuncArgs -> ArgCount -> IO Int64 Base.funcArgInt64 FuncArgs args ArgCount ca' ColumnType Base.FloatColumn -> Double -> SQLData SQLFloat (Double -> SQLData) -> IO Double -> IO SQLData forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b <$> FuncArgs -> ArgCount -> IO Double Base.funcArgDouble FuncArgs args ArgCount ca' ColumnType Base.TextColumn -> (\(Base.Utf8 ByteString b) -> Text -> SQLData SQLText (Text -> SQLData) -> Text -> SQLData forall a b. (a -> b) -> a -> b $ ByteString -> Text TE.decodeUtf8 ByteString b) (Utf8 -> SQLData) -> IO Utf8 -> IO SQLData forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b <$> FuncArgs -> ArgCount -> IO Utf8 Base.funcArgText FuncArgs args ArgCount ca' ColumnType Base.BlobColumn -> ByteString -> SQLData SQLBlob (ByteString -> SQLData) -> IO ByteString -> IO SQLData forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b <$> FuncArgs -> ArgCount -> IO ByteString Base.funcArgBlob FuncArgs args ArgCount ca' ColumnType Base.NullColumn -> SQLData -> IO SQLData forall (f :: * -> *) a. Applicative f => a -> f a pure SQLData SQLNull case FieldParser f forall a. FromField a => FieldParser a fromField FieldParser f -> FieldParser f forall a b. (a -> b) -> a -> b $ SQLData -> Int -> Field Field SQLData sqlv Int ca of Ok f arg -> FuncContext -> FuncArgs -> Int -> r -> IO () forall a. Function a => FuncContext -> FuncArgs -> Int -> a -> IO () evalFunction FuncContext ctx FuncArgs args (Int ca Int -> Int -> Int forall a. Num a => a -> a -> a + Int 1) (f -> r fn f arg) Errors [SomeException] ex -> ManyErrors -> IO () forall a e. Exception e => e -> a throw (ManyErrors -> IO ()) -> ManyErrors -> IO () forall a b. (a -> b) -> a -> b $ [SomeException] -> ManyErrors ManyErrors [SomeException] ex createFunction :: forall f . Function f => Connection -> T.Text -> f -> IO (Either Base.Error ()) createFunction :: Connection -> Text -> f -> IO (Either Error ()) createFunction (Connection Database db) Text fn f f = Database -> Utf8 -> Maybe ArgCount -> Bool -> (FuncContext -> FuncArgs -> IO ()) -> IO (Either Error ()) Base.createFunction Database db (ByteString -> Utf8 Base.Utf8 (ByteString -> Utf8) -> ByteString -> Utf8 forall a b. (a -> b) -> a -> b $ Text -> ByteString TE.encodeUtf8 Text fn) (ArgCount -> Maybe ArgCount forall a. a -> Maybe a Just (ArgCount -> Maybe ArgCount) -> ArgCount -> Maybe ArgCount forall a b. (a -> b) -> a -> b $ Int -> ArgCount Base.ArgCount (Int -> ArgCount) -> Int -> ArgCount forall a b. (a -> b) -> a -> b $ Proxy f -> Int forall a. Function a => Proxy a -> Int argCount (Proxy f forall k (t :: k). Proxy t Proxy :: Proxy f)) (Proxy f -> Bool forall a. Function a => Proxy a -> Bool deterministicFn (Proxy f forall k (t :: k). Proxy t Proxy :: Proxy f)) (\FuncContext ctx FuncArgs args -> IO () -> (SomeException -> IO ()) -> IO () forall e a. Exception e => IO a -> (e -> IO a) -> IO a catch (FuncContext -> FuncArgs -> Int -> f -> IO () forall a. Function a => FuncContext -> FuncArgs -> Int -> a -> IO () evalFunction FuncContext ctx FuncArgs args Int 0 f f) ((IO () -> SomeException -> IO () forall a b. a -> b -> a const :: IO () -> SomeException -> IO ()) (IO () -> SomeException -> IO ()) -> IO () -> SomeException -> IO () forall a b. (a -> b) -> a -> b $ FuncContext -> IO () Base.funcResultNull FuncContext ctx)) deleteFunction :: Connection -> T.Text -> IO (Either Base.Error ()) deleteFunction :: Connection -> Text -> IO (Either Error ()) deleteFunction (Connection Database db) Text fn = Database -> Utf8 -> Maybe ArgCount -> IO (Either Error ()) Base.deleteFunction Database db (ByteString -> Utf8 Base.Utf8 (ByteString -> Utf8) -> ByteString -> Utf8 forall a b. (a -> b) -> a -> b $ Text -> ByteString TE.encodeUtf8 Text fn) Maybe ArgCount forall a. Maybe a Nothing