{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK not-home #-}
module Arbiter.Core.Sql.Query
( Query (..)
, mkQuery
, raw
, param
, rows
, rawRows
, sepBy
, mwhen
, Piece (..)
, ToFragment (..)
) where
import Data.List (intercalate, mapAccumL)
import Data.Text (Text)
import Data.Text qualified as T
import Arbiter.Core.Codec (Params, RowCodec, SomeParam)
import Arbiter.Core.Enum (showText)
data Piece
=
Lit !Text
|
Hole
deriving stock (Piece -> Piece -> Bool
(Piece -> Piece -> Bool) -> (Piece -> Piece -> Bool) -> Eq Piece
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Piece -> Piece -> Bool
== :: Piece -> Piece -> Bool
$c/= :: Piece -> Piece -> Bool
/= :: Piece -> Piece -> Bool
Eq, Int -> Piece -> ShowS
[Piece] -> ShowS
Piece -> String
(Int -> Piece -> ShowS)
-> (Piece -> String) -> ([Piece] -> ShowS) -> Show Piece
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Piece -> ShowS
showsPrec :: Int -> Piece -> ShowS
$cshow :: Piece -> String
show :: Piece -> String
$cshowList :: [Piece] -> ShowS
showList :: [Piece] -> ShowS
Show)
data Query a = Query
{ forall a. Query a -> [Piece]
qPieces :: [Piece]
, forall a. Query a -> Params
qParams :: Params
, forall a. Query a -> Text
qSql :: Text
, forall a. Query a -> Text
qPositional :: Text
, forall a. Query a -> RowCodec a
qDecode :: RowCodec a
}
deriving stock ((forall a b. (a -> b) -> Query a -> Query b)
-> (forall a b. a -> Query b -> Query a) -> Functor Query
forall a b. a -> Query b -> Query a
forall a b. (a -> b) -> Query a -> Query b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> Query a -> Query b
fmap :: forall a b. (a -> b) -> Query a -> Query b
$c<$ :: forall a b. a -> Query b -> Query a
<$ :: forall a b. a -> Query b -> Query a
Functor)
mkQuery :: [Piece] -> Params -> RowCodec a -> Query a
mkQuery :: forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery [Piece]
pieces Params
params RowCodec a
decode =
Query
{ qPieces :: [Piece]
qPieces = [Piece]
pieces
, qParams :: Params
qParams = Params
params
, qSql :: Text
qSql = (Int -> Text) -> [Piece] -> Text
render (Text -> Int -> Text
forall a b. a -> b -> a
const Text
"?") [Piece]
pieces
, qPositional :: Text
qPositional = (Int -> Text) -> [Piece] -> Text
render Int -> Text
forall {a}. Show a => a -> Text
positional [Piece]
pieces
, qDecode :: RowCodec a
qDecode = RowCodec a
decode
}
where
positional :: a -> Text
positional a
index = Text
"$" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> a -> Text
forall {a}. Show a => a -> Text
showText a
index
render :: (Int -> Text) -> [Piece] -> Text
render :: (Int -> Text) -> [Piece] -> Text
render Int -> Text
hole = [Text] -> Text
T.concat ([Text] -> Text) -> ([Piece] -> [Text]) -> [Piece] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, [Text]) -> [Text]
forall a b. (a, b) -> b
snd ((Int, [Text]) -> [Text])
-> ([Piece] -> (Int, [Text])) -> [Piece] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Piece -> (Int, Text)) -> Int -> [Piece] -> (Int, [Text])
forall (t :: * -> *) s a b.
Traversable t =>
(s -> a -> (s, b)) -> s -> t a -> (s, t b)
mapAccumL Int -> Piece -> (Int, Text)
step Int
1
where
step :: Int -> Piece -> (Int, Text)
step !Int
index (Lit Text
literal) = (Int
index, Text
literal)
step !Int
index Piece
Hole = (Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, Int -> Text
hole Int
index)
instance Semigroup (Query ()) where
Query ()
query1 <> :: Query () -> Query () -> Query ()
<> Query ()
query2 = [Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery (Query () -> [Piece]
forall a. Query a -> [Piece]
qPieces Query ()
query1 [Piece] -> [Piece] -> [Piece]
forall a. Semigroup a => a -> a -> a
<> Query () -> [Piece]
forall a. Query a -> [Piece]
qPieces Query ()
query2) (Query () -> Params
forall a. Query a -> Params
qParams Query ()
query1 Params -> Params -> Params
forall a. Semigroup a => a -> a -> a
<> Query () -> Params
forall a. Query a -> Params
qParams Query ()
query2) (() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
instance Monoid (Query ()) where
mempty :: Query ()
mempty = [Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery [] [] (() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
mconcat :: [Query ()] -> Query ()
mconcat [Query ()]
queries = [Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery ((Query () -> [Piece]) -> [Query ()] -> [Piece]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Query () -> [Piece]
forall a. Query a -> [Piece]
qPieces [Query ()]
queries) ((Query () -> Params) -> [Query ()] -> Params
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Query () -> Params
forall a. Query a -> Params
qParams [Query ()]
queries) (() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
raw :: Text -> Query ()
raw :: Text -> Query ()
raw Text
literal = [Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery [Text -> Piece
Lit Text
literal] [] (() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
param :: SomeParam -> Query ()
param :: SomeParam -> Query ()
param SomeParam
value = [Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery [Piece
Hole] [SomeParam
value] (() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
rows :: RowCodec a -> Query () -> Query a
rows :: forall a. RowCodec a -> Query () -> Query a
rows RowCodec a
decoder Query ()
query = Query ()
query {qDecode = decoder}
rawRows :: RowCodec a -> Text -> Query a
rawRows :: forall a. RowCodec a -> Text -> Query a
rawRows RowCodec a
decoder = RowCodec a -> Query () -> Query a
forall a. RowCodec a -> Query () -> Query a
rows RowCodec a
decoder (Query () -> Query a) -> (Text -> Query ()) -> Text -> Query a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Query ()
raw
sepBy :: Text -> [Query ()] -> Query ()
sepBy :: Text -> [Query ()] -> Query ()
sepBy Text
sep [Query ()]
queries =
[Piece] -> Params -> RowCodec () -> Query ()
forall a. [Piece] -> Params -> RowCodec a -> Query a
mkQuery
([Piece] -> [[Piece]] -> [Piece]
forall a. [a] -> [[a]] -> [a]
intercalate [Text -> Piece
Lit Text
sep] ((Query () -> [Piece]) -> [Query ()] -> [[Piece]]
forall a b. (a -> b) -> [a] -> [b]
map Query () -> [Piece]
forall a. Query a -> [Piece]
qPieces [Query ()]
queries))
((Query () -> Params) -> [Query ()] -> Params
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Query () -> Params
forall a. Query a -> Params
qParams [Query ()]
queries)
(() -> RowCodec ()
forall a. a -> Ap NullCol a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
mwhen :: (Monoid m) => Bool -> m -> m
mwhen :: forall m. Monoid m => Bool -> m -> m
mwhen Bool
True m
fragment = m
fragment
mwhen Bool
False m
_ = m
forall a. Monoid a => a
mempty
class ToFragment a where
toFragment :: a -> Query ()
instance ToFragment Text where
toFragment :: Text -> Query ()
toFragment = Text -> Query ()
raw
instance ToFragment (Query ()) where
toFragment :: Query () -> Query ()
toFragment = Query () -> Query ()
forall a. a -> a
id