{-# LANGUAGE MagicHash #-}
{-# OPTIONS_HADDOCK not-home #-}
module Hmssql.Query.Types
(
Query (..)
, Node (..)
, piece
, Fragment (..)
, QueryParam (..)
, queryFragments
, queryParams
, queryText
, renderFragments
, placeholderText
, queryProblem
, fragmentsProblem
, Request (..)
, ResponseReader (..)
, BatchScope (..)
, RowBatch (..)
, ResponseStep (..)
, ResultSet (..)
, ResultSets (..)
) where
import Data.IORef (IORef)
import Data.String (IsString (..))
import Data.Text (Text)
import qualified Data.Text as T
import Data.Void (Void)
import Hmssql.Internal.Derived
import Hmssql.Tds.PacketWriter (PacketWriter)
import Hmssql.Connection.Internal (ActiveResponse, Connection, ResponseStep (..), RowBatch (..))
import Hmssql.Encoding (EncodedParam)
import Hmssql.Query.Lexer
import Hmssql.Tds.Context (TdsContext)
import Hmssql.Tds.Packet (PacketType)
import Hmssql.Tds.Token (ColumnInfo, Done, ServerMessage, Token)
data Query = Query
{ Query -> Int
queryPositional :: !Int
, Query -> Node
queryNode :: Node
}
data Node
= Empty
| Piece [Fragment] [QueryParam]
| Append Query Query
data QueryParam = QueryParam
{ QueryParam -> Text
paramName :: !Text
, QueryParam -> TdsContext -> Either [Char] EncodedParam
paramEncode :: TdsContext -> Either String EncodedParam
}
instance Show QueryParam where
showsPrec :: Int -> QueryParam -> ShowS
showsPrec Int
d QueryParam
p = Int -> Addr# -> [Arg] -> ShowS
showCon Int
d Addr#
"QueryParam"# [Text -> Arg
forall a. Show a => a -> Arg
Arg (QueryParam -> Text
paramName QueryParam
p), Verbatim -> Arg
forall a. Show a => a -> Arg
Arg ([Char] -> Verbatim
Verbatim [Char]
"<value>")]
{-# OPAQUE showsPrec #-}
piece :: [Fragment] -> [QueryParam] -> Query
piece :: [Fragment] -> [QueryParam] -> Query
piece [] [] = Query
forall a. Monoid a => a
mempty
piece [Fragment]
frags [QueryParam]
params = Int -> Node -> Query
Query ([QueryParam] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((QueryParam -> Bool) -> [QueryParam] -> [QueryParam]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Bool
T.null (Text -> Bool) -> (QueryParam -> Text) -> QueryParam -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QueryParam -> Text
paramName) [QueryParam]
params)) ([Fragment] -> [QueryParam] -> Node
Piece [Fragment]
frags [QueryParam]
params)
instance Semigroup Query where
Query Int
_ Node
Empty <> :: Query -> Query -> Query
<> Query
b = Query
b
Query
a <> Query Int
_ Node
Empty = Query
a
Query
a <> Query
b = Int -> Node -> Query
Query (Query -> Int
queryPositional Query
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Query -> Int
queryPositional Query
b) (Query -> Query -> Node
Append Query
a Query
b)
{-# NOINLINE (<>) #-}
instance Monoid Query where
mempty :: Query
mempty = Int -> Node -> Query
Query Int
0 Node
Empty
instance IsString Query where
fromString :: [Char] -> Query
fromString [Char]
s = [Fragment] -> [QueryParam] -> Query
piece [Text -> Fragment
Static ([Char] -> Text
T.pack [Char]
s) | Bool -> Bool
not ([Char] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
s)] []
instance Show Query where
showsPrec :: Int -> Query -> ShowS
showsPrec Int
d Query
q = Int -> Text -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
d (Query -> Text
queryText Query
q)
queryFragments :: Query -> [Fragment]
queryFragments :: Query -> [Fragment]
queryFragments Query
q = Int -> Query -> [Fragment] -> [Fragment]
go Int
0 Query
q []
where
go :: Int -> Query -> [Fragment] -> [Fragment]
go !Int
off (Query Int
_ Node
node) [Fragment]
rest = case Node
node of
Node
Empty -> [Fragment]
rest
Piece [Fragment]
frags [QueryParam]
_ -> Int -> [Fragment] -> [Fragment] -> [Fragment]
shiftAll Int
off [Fragment]
frags [Fragment]
rest
Append Query
l Query
r -> let !rest' :: [Fragment]
rest' = Int -> Query -> [Fragment] -> [Fragment]
go (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Query -> Int
queryPositional Query
l) Query
r [Fragment]
rest in Int -> Query -> [Fragment] -> [Fragment]
go Int
off Query
l [Fragment]
rest'
shiftAll :: Int -> [Fragment] -> [Fragment] -> [Fragment]
shiftAll !Int
off [Fragment]
frags [Fragment]
rest = case [Fragment]
frags of
[] -> [Fragment]
rest
Fragment
f : [Fragment]
fs -> let !more :: [Fragment]
more = Int -> [Fragment] -> [Fragment] -> [Fragment]
shiftAll Int
off [Fragment]
fs [Fragment]
rest; !f' :: Fragment
f' = Int -> Fragment -> Fragment
shift Int
off Fragment
f in Fragment
f' Fragment -> [Fragment] -> [Fragment]
forall a. a -> [a] -> [a]
: [Fragment]
more
shift :: Int -> Fragment -> Fragment
shift Int
0 Fragment
f = Fragment
f
shift Int
off (Placeholder Int
n) = Int -> Fragment
Placeholder (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
off)
shift Int
_ Fragment
f = Fragment
f
queryParams :: Query -> [QueryParam]
queryParams :: Query -> [QueryParam]
queryParams Query
q = Query -> [QueryParam] -> [QueryParam]
go Query
q []
where
go :: Query -> [QueryParam] -> [QueryParam]
go (Query Int
_ Node
node) [QueryParam]
rest = case Node
node of
Node
Empty -> [QueryParam]
rest
Piece [Fragment]
_ [QueryParam]
params -> [QueryParam]
params [QueryParam] -> [QueryParam] -> [QueryParam]
forall a. [a] -> [a] -> [a]
++ [QueryParam]
rest
Append Query
l Query
r -> Query -> [QueryParam] -> [QueryParam]
go Query
l (Query -> [QueryParam] -> [QueryParam]
go Query
r [QueryParam]
rest)
queryText :: Query -> Text
queryText :: Query -> Text
queryText = [Fragment] -> Text
renderFragments ([Fragment] -> Text) -> (Query -> [Fragment]) -> Query -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> [Fragment]
queryFragments
renderFragments :: [Fragment] -> Text
renderFragments :: [Fragment] -> Text
renderFragments = \case
[] -> Text
T.empty
[Static Text
t] -> Text
t
[Fragment]
frags -> [Text] -> Text
T.concat (Char -> [Fragment] -> [Text]
go Char
'\0' [Fragment]
frags)
where
go :: Char -> [Fragment] -> [Text]
go Char
_ [] = []
go Char
prev (Fragment
f : [Fragment]
fs) = case Fragment
f of
Static Text
t -> Text
t Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: Char -> [Fragment] -> [Text]
go (if Text -> Bool
T.null Text
t then Char
prev else HasCallStack => Text -> Char
Text -> Char
T.last Text
t) [Fragment]
fs
Placeholder Int
n ->
(if Char -> Bool
isVariableChar Char
prev then [Text
" "] else [])
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ Int -> Text
placeholderText Int
n
Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: (if [Fragment] -> Bool
spaceAfter [Fragment]
fs then [Text
" "] else [])
[Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ Char -> [Fragment] -> [Text]
go Char
' ' [Fragment]
fs
spaceAfter :: [Fragment] -> Bool
spaceAfter = \case
Static Text
t : [Fragment]
_ | Just (Char
c, Text
_) <- Text -> Maybe (Char, Text)
T.uncons Text
t -> Char -> Bool
isVariableChar Char
c
Placeholder Int
_ : [Fragment]
_ -> Bool
True
[Fragment]
_ -> Bool
False
placeholderText :: Int -> Text
placeholderText :: Int -> Text
placeholderText Int
n = [Char] -> Text
T.pack (Char
'@' Char -> ShowS
forall a. a -> [a] -> [a]
: Char
'p' Char -> ShowS
forall a. a -> [a] -> [a]
: Int -> [Char]
forall a. Show a => a -> [Char]
show Int
n)
queryProblem :: Query -> Maybe String
queryProblem :: Query -> Maybe [Char]
queryProblem Query
q = Int -> [Fragment] -> Maybe [Char]
fragmentsProblem (Query -> Int
queryPositional Query
q) (Query -> [Fragment]
queryFragments Query
q)
fragmentsProblem :: Int -> [Fragment] -> Maybe String
fragmentsProblem :: Int -> [Fragment] -> Maybe [Char]
fragmentsProblem Int
count [Fragment]
frags = case [Int
n | Placeholder Int
n <- [Fragment]
frags, Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 Bool -> Bool -> Bool
|| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
count] of
[] -> Maybe [Char]
forall a. Maybe a
Nothing
Int
n : [Int]
_ -> [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
[Char]
"the query refers to @p" [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
n [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" but has "
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ (case Int
count of
Int
0 -> [Char]
"no positional parameters"
Int
1 -> [Char]
"one positional parameter"
Int
_ -> Int -> [Char]
forall a. Show a => a -> [Char]
show Int
count [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" positional parameters")
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ (if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 then [Char]
" (they are numbered from 1)" else [Char]
"")
data Request = Request
{ Request -> PacketType
requestPacketType :: !PacketType
, Request -> TdsContext -> PacketWriter -> IO ()
requestBody :: !(TdsContext -> PacketWriter -> IO ())
, Request -> Maybe Text
requestStatement :: !(Maybe Text)
}
data ResponseReader = ResponseReader
{ ResponseReader -> Connection
respConn :: !Connection
, ResponseReader -> ActiveResponse
respActive :: !ActiveResponse
, ResponseReader -> IORef (Maybe ServerMessage)
respFirstError :: !(IORef (Maybe ServerMessage))
, ResponseReader -> IORef (Maybe Done)
respFailedDone :: !(IORef (Maybe Done))
, ResponseReader -> IORef (Maybe Token)
respPushback :: !(IORef (Maybe Token))
, ResponseReader -> Maybe Text
respStatement :: !(Maybe Text)
, ResponseReader -> IORef Bool
respEnded :: !(IORef Bool)
, ResponseReader -> Maybe BatchScope
respBatch :: !(Maybe BatchScope)
}
data ResultSet r = ResultSet
{ forall r. ResultSet r -> [ColumnInfo]
resultColumns :: ![ColumnInfo]
, forall r. ResultSet r -> [r]
resultRows :: ![r]
}
deriving stock (ResultSet r -> ResultSet r -> Bool
(ResultSet r -> ResultSet r -> Bool)
-> (ResultSet r -> ResultSet r -> Bool) -> Eq (ResultSet r)
forall r. Eq r => ResultSet r -> ResultSet r -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall r. Eq r => ResultSet r -> ResultSet r -> Bool
== :: ResultSet r -> ResultSet r -> Bool
$c/= :: forall r. Eq r => ResultSet r -> ResultSet r -> Bool
/= :: ResultSet r -> ResultSet r -> Bool
Eq, (forall a b. (a -> b) -> ResultSet a -> ResultSet b)
-> (forall a b. a -> ResultSet b -> ResultSet a)
-> Functor ResultSet
forall a b. a -> ResultSet b -> ResultSet a
forall a b. (a -> b) -> ResultSet a -> ResultSet 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) -> ResultSet a -> ResultSet b
fmap :: forall a b. (a -> b) -> ResultSet a -> ResultSet b
$c<$ :: forall a b. a -> ResultSet b -> ResultSet a
<$ :: forall a b. a -> ResultSet b -> ResultSet a
Functor)
instance Show r => Show (ResultSet r) where
showsPrec :: Int -> ResultSet r -> ShowS
showsPrec Int
p (ResultSet [ColumnInfo]
a [r]
b) =
Int -> Addr# -> [Field] -> ShowS
showRecord Int
p Addr#
"ResultSet"# [Addr#
"resultColumns"# Addr# -> [ColumnInfo] -> Field
forall a. Show a => Addr# -> a -> Field
:= [ColumnInfo]
a, Addr#
"resultRows"# Addr# -> [r] -> Field
forall a. Show a => Addr# -> a -> Field
:= [r]
b]
{-# OPAQUE showsPrec #-}
newtype ResultSets a = ResultSets
{ forall a. ResultSets a -> TdsContext -> ResponseReader -> IO a
runResultSets :: TdsContext -> ResponseReader -> IO a
}
instance Functor ResultSets where
fmap :: forall a b. (a -> b) -> ResultSets a -> ResultSets b
fmap a -> b
f (ResultSets TdsContext -> ResponseReader -> IO a
m) = (TdsContext -> ResponseReader -> IO b) -> ResultSets b
forall a. (TdsContext -> ResponseReader -> IO a) -> ResultSets a
ResultSets ((TdsContext -> ResponseReader -> IO b) -> ResultSets b)
-> (TdsContext -> ResponseReader -> IO b) -> ResultSets b
forall a b. (a -> b) -> a -> b
$ \TdsContext
ctx ResponseReader
resp -> a -> b
f (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TdsContext -> ResponseReader -> IO a
m TdsContext
ctx ResponseReader
resp
{-# NOINLINE fmap #-}
instance Applicative ResultSets where
pure :: forall a. a -> ResultSets a
pure a
a = (TdsContext -> ResponseReader -> IO a) -> ResultSets a
forall a. (TdsContext -> ResponseReader -> IO a) -> ResultSets a
ResultSets ((TdsContext -> ResponseReader -> IO a) -> ResultSets a)
-> (TdsContext -> ResponseReader -> IO a) -> ResultSets a
forall a b. (a -> b) -> a -> b
$ \TdsContext
_ ResponseReader
_ -> a -> IO a
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a
ResultSets (a -> b)
mf <*> :: forall a b. ResultSets (a -> b) -> ResultSets a -> ResultSets b
<*> ResultSets a
ma = ResultSets (a -> b)
mf ResultSets (a -> b) -> ((a -> b) -> ResultSets b) -> ResultSets b
forall a b. ResultSets a -> (a -> ResultSets b) -> ResultSets b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \a -> b
f -> (a -> b) -> ResultSets a -> ResultSets b
forall a b. (a -> b) -> ResultSets a -> ResultSets b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f ResultSets a
ma
{-# NOINLINE (<*>) #-}
liftA2 :: forall a b c.
(a -> b -> c) -> ResultSets a -> ResultSets b -> ResultSets c
liftA2 a -> b -> c
f ResultSets a
ma ResultSets b
mb = ResultSets a
ma ResultSets a -> (a -> ResultSets c) -> ResultSets c
forall a b. ResultSets a -> (a -> ResultSets b) -> ResultSets b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \a
a -> (b -> c) -> ResultSets b -> ResultSets c
forall a b. (a -> b) -> ResultSets a -> ResultSets b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (a -> b -> c
f a
a) ResultSets b
mb
{-# NOINLINE liftA2 #-}
instance Monad ResultSets where
ResultSets TdsContext -> ResponseReader -> IO a
m >>= :: forall a b. ResultSets a -> (a -> ResultSets b) -> ResultSets b
>>= a -> ResultSets b
k = (TdsContext -> ResponseReader -> IO b) -> ResultSets b
forall a. (TdsContext -> ResponseReader -> IO a) -> ResultSets a
ResultSets ((TdsContext -> ResponseReader -> IO b) -> ResultSets b)
-> (TdsContext -> ResponseReader -> IO b) -> ResultSets b
forall a b. (a -> b) -> a -> b
$ \TdsContext
ctx ResponseReader
resp -> do
a <- TdsContext -> ResponseReader -> IO a
m TdsContext
ctx ResponseReader
resp
runResultSets (k a) ctx resp
{-# NOINLINE (>>=) #-}
data BatchScope = BatchScope
{ BatchScope -> IORef Bool
scopeEnded :: !(IORef Bool)
, BatchScope -> IORef Text
scopeStatement :: !(IORef Text)
, BatchScope -> ServerMessage -> IO Void
scopeFail :: ServerMessage -> IO Void
}