module Hpgsql.Query
( Query,
SingleQuery,
sql,
sqlPrep,
mkQuery,
escapeIdentifier,
vALUES,
preparedStatement,
nonPreparedStatement,
breakQueryIntoStatements,
mkQueryInternal,
encodeParam,
)
where
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.List as List
import Data.Proxy (Proxy (..))
import Hpgsql.Builder (BinaryField (..))
import Hpgsql.Encoding (RowEncoder (..), ToPgRow (..))
import Hpgsql.InternalTypes (Query (..), SingleQuery (..), SingleQueryFragment (..), breakQueryIntoStatements)
import Hpgsql.QueryInternal (encodeParam, mkQuery, mkQueryInternal, sql, sqlPrep)
import Hpgsql.TypeInfo (EncodingContext, Oid)
escapeIdentifier :: ByteString -> Query
escapeIdentifier :: ByteString -> Query
escapeIdentifier ByteString
v = Query {queryString :: [SingleQueryFragment]
queryString = [ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
"\"", ByteString -> SingleQueryFragment
FragmentOfStaticSql (ByteString -> ByteString
doubleQuotes ByteString
v), ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
"\""], queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
queryParams = [], isPrepared :: Bool
isPrepared = Bool
False}
where
doubleQuotes :: ByteString -> ByteString
doubleQuotes = ByteString -> [ByteString] -> ByteString
BS.intercalate ByteString
"\"\"" ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> ByteString -> [ByteString]
BS.split Word8
0x22
vALUES :: forall a. (ToPgRow a) => [a] -> Query
vALUES :: forall a. ToPgRow a => [a] -> Query
vALUES [] =
let rowOids :: [EncodingContext -> Maybe Oid]
rowOids = RowEncoder a
forall a. ToPgRow a => RowEncoder a
rowEncoder.toTypeOids (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @a)
nullParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
nullParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
nullParams = ((EncodingContext -> Maybe Oid)
-> EncodingContext -> (Maybe Oid, BinaryField))
-> [EncodingContext -> Maybe Oid]
-> [EncodingContext -> (Maybe Oid, BinaryField)]
forall a b. (a -> b) -> [a] -> [b]
map (\EncodingContext -> Maybe Oid
f -> \EncodingContext
encCtx -> (EncodingContext -> Maybe Oid
f EncodingContext
encCtx, BinaryField
SqlNull)) [EncodingContext -> Maybe Oid]
rowOids
in [sql|(SELECT * FROM (VALUES ^{commaSeparatedRowTuples [nullParams]}) _subq LIMIT 0)|]
vALUES [a]
rows =
let allParams :: [[EncodingContext -> (Maybe Oid, BinaryField)]]
allParams = (a -> [EncodingContext -> (Maybe Oid, BinaryField)])
-> [a] -> [[EncodingContext -> (Maybe Oid, BinaryField)]]
forall a b. (a -> b) -> [a] -> [b]
map RowEncoder a
forall a. ToPgRow a => RowEncoder a
rowEncoder.toPgParams [a]
rows
in Query
"VALUES " Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> [[EncodingContext -> (Maybe Oid, BinaryField)]] -> Query
commaSeparatedRowTuples [[EncodingContext -> (Maybe Oid, BinaryField)]]
allParams
commaSeparatedRowTuples :: [[EncodingContext -> (Maybe Oid, BinaryField)]] -> Query
commaSeparatedRowTuples :: [[EncodingContext -> (Maybe Oid, BinaryField)]] -> Query
commaSeparatedRowTuples [[EncodingContext -> (Maybe Oid, BinaryField)]]
rowTuples =
let (Int
_, [[SingleQueryFragment]]
queryFragsPerRow) =
(Int
-> [EncodingContext -> (Maybe Oid, BinaryField)]
-> (Int, [SingleQueryFragment]))
-> Int
-> [[EncodingContext -> (Maybe Oid, BinaryField)]]
-> (Int, [[SingleQueryFragment]])
forall (t :: * -> *) s a b.
Traversable t =>
(s -> a -> (s, b)) -> s -> t a -> (s, t b)
List.mapAccumR
( \(!Int
maxArgSoFar) [EncodingContext -> (Maybe Oid, BinaryField)]
singleRow ->
let numParams :: Int
numParams = [EncodingContext -> (Maybe Oid, BinaryField)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [EncodingContext -> (Maybe Oid, BinaryField)]
singleRow
numberedArgs :: [SingleQueryFragment]
numberedArgs = (Int -> SingleQueryFragment) -> [Int] -> [SingleQueryFragment]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> SingleQueryFragment
QueryArgumentPlaceHolder (Int -> SingleQueryFragment)
-> (Int -> Int) -> Int -> SingleQueryFragment
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
maxArgSoFar)) [Int
1 .. Int
numParams]
in (Int
maxArgSoFar Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
numParams, ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
"(" SingleQueryFragment
-> [SingleQueryFragment] -> [SingleQueryFragment]
forall a. a -> [a] -> [a]
: (SingleQueryFragment
-> [SingleQueryFragment] -> [SingleQueryFragment]
forall a. a -> [a] -> [a]
List.intersperse (ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
",") [SingleQueryFragment]
numberedArgs [SingleQueryFragment]
-> [SingleQueryFragment] -> [SingleQueryFragment]
forall a. [a] -> [a] -> [a]
++ [ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
")"]))
)
Int
0
[[EncodingContext -> (Maybe Oid, BinaryField)]]
rowTuples
in Query {queryString :: [SingleQueryFragment]
queryString = [[SingleQueryFragment]] -> [SingleQueryFragment]
forall a. Monoid a => [a] -> a
mconcat ([[SingleQueryFragment]] -> [SingleQueryFragment])
-> [[SingleQueryFragment]] -> [SingleQueryFragment]
forall a b. (a -> b) -> a -> b
$ [SingleQueryFragment]
-> [[SingleQueryFragment]] -> [[SingleQueryFragment]]
forall a. a -> [a] -> [a]
List.intersperse [ByteString -> SingleQueryFragment
FragmentOfStaticSql ByteString
","] [[SingleQueryFragment]]
queryFragsPerRow, queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
queryParams = [[EncodingContext -> (Maybe Oid, BinaryField)]]
-> [EncodingContext -> (Maybe Oid, BinaryField)]
forall a. Monoid a => [a] -> a
mconcat [[EncodingContext -> (Maybe Oid, BinaryField)]]
rowTuples, isPrepared :: Bool
isPrepared = Bool
False}
preparedStatement :: Query -> Query
preparedStatement :: Query -> Query
preparedStatement Query {Bool
[SingleQueryFragment]
[EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: Query -> [SingleQueryFragment]
queryParams :: Query -> [EncodingContext -> (Maybe Oid, BinaryField)]
isPrepared :: Query -> Bool
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
isPrepared :: Bool
..} = Query {isPrepared :: Bool
isPrepared = Bool
True, [SingleQueryFragment]
[EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
..}
nonPreparedStatement :: Query -> Query
nonPreparedStatement :: Query -> Query
nonPreparedStatement Query {Bool
[SingleQueryFragment]
[EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: Query -> [SingleQueryFragment]
queryParams :: Query -> [EncodingContext -> (Maybe Oid, BinaryField)]
isPrepared :: Query -> Bool
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
isPrepared :: Bool
..} = Query {isPrepared :: Bool
isPrepared = Bool
False, [SingleQueryFragment]
[EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
queryString :: [SingleQueryFragment]
queryParams :: [EncodingContext -> (Maybe Oid, BinaryField)]
..}