  | 
 Copyright    : ( c ) Runtime Verification , 2020 - 2021 
 License      : BSD-3 - Clause 

Copyright   : (c) Runtime Verification, 2020-2021
License     : BSD-3-Clause
-}
module SQL.SQL (
    SQL (..),
    runSQL,
    execute_,
    execute,
    executeNamed,
    query,
    queryNamed,
    lastInsertRowId,

    -- * Re-exports
    Query,
    SQLData (..),
) where

import Control.Monad.Catch qualified as Exceptions
import Control.Monad.Reader (
    ReaderT (ReaderT),
    runReaderT,
 )
import Data.Int (
    Int64,
 )
import Database.SQLite.Simple (
    FromRow,
    NamedParam,
    Query,
    SQLData (..),
 )
import Database.SQLite.Simple qualified as SQLite
import Prelude.Kore

-- | @SQL@ is a 'Monad' for executing SQL statements.
newtype SQL a = SQL {getSQL :: ReaderT SQLite.Connection IO a}
    deriving newtype (Functor, Applicative, Monad)
    deriving newtype (MonadIO)

instance (Semigroup a) => Semigroup (SQL a) where
    (<>) sqlt1 sqlt2 = SQL $ (<>) <$> getSQL sqlt1 <*> getSQL sqlt2

instance (Monoid a) => Monoid (SQL a) where
    mempty = pure mempty

-- | Run the given sequence of statements in the named database.
runSQL ::
  | SQLite database
    FilePath ->
    -- | statements
    SQL a ->
    IO a
runSQL filePath =
    Exceptions.bracket
        (liftIO $ SQLite.open filePath)
        (liftIO . SQLite.close)
        . runReaderT
        . getSQL

execute_ :: Query -> SQL ()
execute_ q = SQL . ReaderT $ \conn -> SQLite.execute_ conn q

execute :: Query -> [SQLData] -> SQL ()
execute q values = SQL . ReaderT $ \conn -> SQLite.execute conn q values

executeNamed :: Query -> [NamedParam] -> SQL ()
executeNamed q params =
    SQL . ReaderT $ \conn -> SQLite.executeNamed conn q params

lastInsertRowId :: SQL Int64
lastInsertRowId = SQL . ReaderT $ SQLite.lastInsertRowId

queryNamed :: FromRow r => Query -> [NamedParam] -> SQL [r]
queryNamed q params =
    SQL . ReaderT $ \conn -> SQLite.queryNamed conn q params

query :: FromRow r => Query -> [SQLData] -> SQL [r]
query q params =
    SQL . ReaderT $ \conn -> SQLite.query conn q params

 * Re-exports
 | @SQL@ is a 'Monad' for executing SQL statements.
 | Run the given sequence of statements in the named database.
 | statements
  | 
 Copyright    : ( c ) Runtime Verification , 2020 - 2021 
 License      : BSD-3 - Clause 

Copyright   : (c) Runtime Verification, 2020-2021
License     : BSD-3-Clause
-}
module SQL.SQL (
    SQL (..),
    runSQL,
    execute_,
    execute,
    executeNamed,
    query,
    queryNamed,
    lastInsertRowId,

    Query,
    SQLData (..),
) where

import Control.Monad.Catch qualified as Exceptions
import Control.Monad.Reader (
    ReaderT (ReaderT),
    runReaderT,
 )
import Data.Int (
    Int64,
 )
import Database.SQLite.Simple (
    FromRow,
    NamedParam,
    Query,
    SQLData (..),
 )
import Database.SQLite.Simple qualified as SQLite
import Prelude.Kore

newtype SQL a = SQL {getSQL :: ReaderT SQLite.Connection IO a}
    deriving newtype (Functor, Applicative, Monad)
    deriving newtype (MonadIO)

instance (Semigroup a) => Semigroup (SQL a) where
    (<>) sqlt1 sqlt2 = SQL $ (<>) <$> getSQL sqlt1 <*> getSQL sqlt2

instance (Monoid a) => Monoid (SQL a) where
    mempty = pure mempty

runSQL ::
  | SQLite database
    FilePath ->
    SQL a ->
    IO a
runSQL filePath =
    Exceptions.bracket
        (liftIO $ SQLite.open filePath)
        (liftIO . SQLite.close)
        . runReaderT
        . getSQL

execute_ :: Query -> SQL ()
execute_ q = SQL . ReaderT $ \conn -> SQLite.execute_ conn q

execute :: Query -> [SQLData] -> SQL ()
execute q values = SQL . ReaderT $ \conn -> SQLite.execute conn q values

executeNamed :: Query -> [NamedParam] -> SQL ()
executeNamed q params =
    SQL . ReaderT $ \conn -> SQLite.executeNamed conn q params

lastInsertRowId :: SQL Int64
lastInsertRowId = SQL . ReaderT $ SQLite.lastInsertRowId

queryNamed :: FromRow r => Query -> [NamedParam] -> SQL [r]
queryNamed q params =
    SQL . ReaderT $ \conn -> SQLite.queryNamed conn q params

query :: FromRow r => Query -> [SQLData] -> SQL [r]
query q params =
    SQL . ReaderT $ \conn -> SQLite.query conn q params
