{- |

Copyright : Flipstone Technology Partners 2023
License   : MIT
Stability : Stable

The functions in this module are named with the intent that it is imported
qualified as 'RawSql'.

@since 1.0.0.0
-}
module Orville.PostgreSQL.Raw.RawSql
  ( RawSql
  , parameter
  , fromString
  , fromText
  , fromBytes
  , intercalate
  , execute
  , executeVoid
  , connectionQuoting

    -- * Fragments provided for convenience
  , space
  , comma
  , commaSpace
  , leftParen
  , rightParen
  , dot
  , doubleQuote
  , doubleColon
  , stringLiteral
  , identifier
  , parenthesized

    -- * Integer values as literals
  , intDecLiteral
  , int8DecLiteral
  , int16DecLiteral
  , int32DecLiteral
  , int64DecLiteral

    -- * Generic interface for generating SQL
  , SqlExpression (toRawSql, unsafeFromRawSql)
  , unsafeSqlExpression
  , toBytesAndParams
  , toExampleBytes
  , Quoting (Quoting, quoteStringLiteral, quoteIdentifier)
  , exampleQuoting
  )
where

import Control.Monad (void)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as LBS
import Data.DList (DList)
import qualified Data.DList as DList
import qualified Data.Foldable as Fold
import Data.Functor.Identity (Identity (Identity, runIdentity))
import qualified Data.Int as Int
import qualified Data.List as List
import qualified Data.Text as T
import qualified Data.Text.Encoding as TextEnc
import qualified Database.PostgreSQL.LibPQ as LibPQ

import qualified Orville.PostgreSQL.Raw.Connection as Conn
import Orville.PostgreSQL.Raw.PgTextFormatValue (PgTextFormatValue)
import Orville.PostgreSQL.Raw.SqlValue (SqlValue)
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue

{- |
  'RawSql' provides a type for efficiently constructing raw SQL statements from
  smaller parts and then executing them. It also supports using placeholder
  values to pass parameters with a query without having to interpolate them as
  part of the actual SQL state and being exposed to SQL injection.

@since 1.0.0.0
-}
data RawSql
  = SqlSection BSB.Builder
  | Parameter SqlValue
  | StringLiteral BS.ByteString
  | Identifier BS.ByteString
  | Append RawSql RawSql

instance Semigroup RawSql where
  (SqlSection Builder
builderA) <> :: RawSql -> RawSql -> RawSql
<> (SqlSection Builder
builderB) =
    Builder -> RawSql
SqlSection (Builder
builderA Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
builderB)
  RawSql
otherA <> RawSql
otherB =
    RawSql -> RawSql -> RawSql
Append RawSql
otherA RawSql
otherB

instance Monoid RawSql where
  mempty :: RawSql
mempty = Builder -> RawSql
SqlSection Builder
forall a. Monoid a => a
mempty

{- |
 'SqlExpression' provides a common interface for converting types to and from
 'RawSql', either via 'toRawSql' and 'unsafeFromRawSql', or the convenience
 function 'unsafeSqlExpression'. Orville defines a large number of types that
 represent various fragments of SQL statements as well as functions to help
 construct them safely. These functions can be found in
 'Orville.PostgreSQL.Expr'. These types all provide 'SqlExpression' instances
 as an escape hatch to allow you to pass any SQL you wish in place of what
 Orville directly supports. This should be used with great care as Orville
 cannot guarantee that the SQL you pass can be used to generate valid SQL in
 conjunction with the rest of the 'Orville.PostgreSQL.Expr' API.

@since 1.0.0.0
-}
class SqlExpression a where
  toRawSql :: a -> RawSql
  unsafeFromRawSql :: RawSql -> a

instance SqlExpression RawSql where
  toRawSql :: RawSql -> RawSql
toRawSql = RawSql -> RawSql
forall a. a -> a
id
  unsafeFromRawSql :: RawSql -> RawSql
unsafeFromRawSql = RawSql -> RawSql
forall a. a -> a
id

{- |
A convenience function for creating an arbitrary 'SqlExpression' from a
'String'. Great care should be exercised when using this function as it cannot
provide any sort of guarantee that the string passed is usable to generate
valid SQL via the rest of Orville's 'Orville.PostgreSQL.Expr' API.

For example, if one wanted build a boolean expression not supported by Orville,
you can do it like so:

> import qualified Orville.PostgreSQL.Expr as Expr
>
> a :: Expr.BooleanExpr
> a RawSql.unsafeSqlExpression "foo BETWEEN 1 AND 3"
@since 1.0.0.0
-}
unsafeSqlExpression :: SqlExpression a => String -> a
unsafeSqlExpression :: forall a. SqlExpression a => String -> a
unsafeSqlExpression =
  RawSql -> a
forall a. SqlExpression a => RawSql -> a
unsafeFromRawSql (RawSql -> a) -> (String -> RawSql) -> String -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> RawSql
fromString

{- |
  Provides procedures for quoting parts of a raw SQL query so that they can be
  safely executed. Quoting may be done in some 'Monad' m, allowing for the use
  of quoting operations provided by 'Conn.Connection', which operates in the
  'IO' monad.

  See 'connectionQuoting' and 'exampleQuoting'.

@since 1.0.0.0
-}
data Quoting m = Quoting
  { forall (m :: * -> *). Quoting m -> ByteString -> m Builder
quoteStringLiteral :: BS.ByteString -> m BSB.Builder
  , forall (m :: * -> *). Quoting m -> ByteString -> m Builder
quoteIdentifier :: BS.ByteString -> m BSB.Builder
  }

{- |
  Quoting done in pure Haskell that is suitable for showing SQL examples,
  but is not guaranteed to be sufficient for all database connections. For
  quoting that is based on the actual connection to the database, see
  'connectionQuoting'.

@since 1.0.0.0
-}
exampleQuoting :: Quoting Identity
exampleQuoting :: Quoting Identity
exampleQuoting =
  Quoting
    { quoteStringLiteral :: ByteString -> Identity Builder
quoteStringLiteral = Builder -> Identity Builder
forall a. a -> Identity a
Identity (Builder -> Identity Builder)
-> (ByteString -> Builder) -> ByteString -> Identity Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> ByteString -> Builder
exampleQuoteString Char
'\''
    , quoteIdentifier :: ByteString -> Identity Builder
quoteIdentifier = Builder -> Identity Builder
forall a. a -> Identity a
Identity (Builder -> Identity Builder)
-> (ByteString -> Builder) -> ByteString -> Identity Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> ByteString -> Builder
exampleQuoteString Char
'"'
    }

exampleQuoteString :: Char -> BS.ByteString -> BSB.Builder
exampleQuoteString :: Char -> ByteString -> Builder
exampleQuoteString Char
quoteChar =
  let
    quote :: Either (Char, ByteString) ByteString
-> Maybe (Char, Either (Char, ByteString) ByteString)
quote (Right ByteString
bs) =
      case ByteString -> Maybe (Char, ByteString)
B8.uncons ByteString
bs of
        Maybe (Char, ByteString)
Nothing ->
          Maybe (Char, Either (Char, ByteString) ByteString)
forall a. Maybe a
Nothing
        Just (Char
char, ByteString
rest) ->
          (Char, Either (Char, ByteString) ByteString)
-> Maybe (Char, Either (Char, ByteString) ByteString)
forall a. a -> Maybe a
Just ((Char, Either (Char, ByteString) ByteString)
 -> Maybe (Char, Either (Char, ByteString) ByteString))
-> (Char, Either (Char, ByteString) ByteString)
-> Maybe (Char, Either (Char, ByteString) ByteString)
forall a b. (a -> b) -> a -> b
$
            if Char
char Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
quoteChar
              then (Char
char, (Char, ByteString) -> Either (Char, ByteString) ByteString
forall a b. a -> Either a b
Left (Char
char, ByteString
rest))
              else (Char
char, ByteString -> Either (Char, ByteString) ByteString
forall a b. b -> Either a b
Right ByteString
rest)
    quote (Left (Char
char, ByteString
rest)) =
      (Char, Either (Char, ByteString) ByteString)
-> Maybe (Char, Either (Char, ByteString) ByteString)
forall a. a -> Maybe a
Just (Char
char, ByteString -> Either (Char, ByteString) ByteString
forall a b. b -> Either a b
Right ByteString
rest)

    quoteBytes :: Builder
quoteBytes =
      Char -> Builder
BSB.char8 Char
quoteChar
  in
    \ByteString
unquoted ->
      Builder
quoteBytes
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ((Either (Char, ByteString) ByteString
 -> Maybe (Char, Either (Char, ByteString) ByteString))
-> Either (Char, ByteString) ByteString -> ByteString
forall a. (a -> Maybe (Char, a)) -> a -> ByteString
B8.unfoldr Either (Char, ByteString) ByteString
-> Maybe (Char, Either (Char, ByteString) ByteString)
quote (ByteString -> Either (Char, ByteString) ByteString
forall a b. b -> Either a b
Right ByteString
unquoted))
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
quoteBytes

{- |
  Quoting done in IO using the quoting functions provided by the connection,
  which can apply quoting based on the specific connection properties.

  If you don't have a connection available and are only planning on using the
  SQL for explanatory or example purposes, see 'exampleQuoting'.

@since 1.0.0.0
-}
connectionQuoting :: Conn.Connection -> Quoting IO
connectionQuoting :: Connection -> Quoting IO
connectionQuoting Connection
connection =
  Quoting
    { quoteStringLiteral :: ByteString -> IO Builder
quoteStringLiteral = Connection -> ByteString -> IO Builder
Conn.quoteStringLiteral Connection
connection
    , quoteIdentifier :: ByteString -> IO Builder
quoteIdentifier = Connection -> ByteString -> IO Builder
Conn.quoteIdentifier Connection
connection
    }

{- |
  Constructs the actual SQL bytestring and parameter values that will be passed
  to the database to execute a 'RawSql' query. Any string literals that are
  included in the SQL expression will be quoted using the given quoting
  directive.

@since 1.0.0.0
-}
toBytesAndParams ::
  (SqlExpression sql, Monad m) =>
  Quoting m ->
  sql ->
  m (BS.ByteString, [Maybe PgTextFormatValue])
toBytesAndParams :: forall sql (m :: * -> *).
(SqlExpression sql, Monad m) =>
Quoting m -> sql -> m (ByteString, [Maybe PgTextFormatValue])
toBytesAndParams Quoting m
quoting sql
sql = do
  (Builder
byteBuilder, ParamsProgress
finalProgress) <-
    Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
forall (m :: * -> *).
Monad m =>
Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
buildSqlWithProgress Quoting m
quoting ParamsProgress
startingProgress (sql -> RawSql
forall a. SqlExpression a => a -> RawSql
toRawSql sql
sql)
  (ByteString, [Maybe PgTextFormatValue])
-> m (ByteString, [Maybe PgTextFormatValue])
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    ( ByteString -> ByteString
LBS.toStrict (Builder -> ByteString
BSB.toLazyByteString Builder
byteBuilder)
    , DList (Maybe PgTextFormatValue) -> [Maybe PgTextFormatValue]
forall a. DList a -> [a]
DList.toList (ParamsProgress -> DList (Maybe PgTextFormatValue)
paramValues ParamsProgress
finalProgress)
    )

{- |
  Builds the bytes that represent the raw SQL. These bytes may not be executable
  on their own, because they may contain placeholders that must be filled in,
  but can be useful for inspecting SQL queries.

@since 1.0.0.0
-}
toExampleBytes :: SqlExpression sql => sql -> BS.ByteString
toExampleBytes :: forall sql. SqlExpression sql => sql -> ByteString
toExampleBytes =
  (ByteString, [Maybe PgTextFormatValue]) -> ByteString
forall a b. (a, b) -> a
fst ((ByteString, [Maybe PgTextFormatValue]) -> ByteString)
-> (sql -> (ByteString, [Maybe PgTextFormatValue]))
-> sql
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Identity (ByteString, [Maybe PgTextFormatValue])
-> (ByteString, [Maybe PgTextFormatValue])
forall a. Identity a -> a
runIdentity (Identity (ByteString, [Maybe PgTextFormatValue])
 -> (ByteString, [Maybe PgTextFormatValue]))
-> (sql -> Identity (ByteString, [Maybe PgTextFormatValue]))
-> sql
-> (ByteString, [Maybe PgTextFormatValue])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Quoting Identity
-> sql -> Identity (ByteString, [Maybe PgTextFormatValue])
forall sql (m :: * -> *).
(SqlExpression sql, Monad m) =>
Quoting m -> sql -> m (ByteString, [Maybe PgTextFormatValue])
toBytesAndParams Quoting Identity
exampleQuoting

{- |
  This is an internal datatype used during the SQL building process to track
  how many params have been seen so that placeholder indices (e.g. '$1', etc)
  can be generated to include in the SQL.

@since 1.0.0.0
-}
data ParamsProgress = ParamsProgress
  { ParamsProgress -> Int
paramCount :: Int
  , ParamsProgress -> DList (Maybe PgTextFormatValue)
paramValues :: DList (Maybe PgTextFormatValue)
  }

{- |
  An initial value for 'ParamsProgress' that indicates no params have been been
  encountered yet.

@since 1.0.0.0
-}
startingProgress :: ParamsProgress
startingProgress :: ParamsProgress
startingProgress =
  ParamsProgress
    { paramCount :: Int
paramCount = Int
0
    , paramValues :: DList (Maybe PgTextFormatValue)
paramValues = DList (Maybe PgTextFormatValue)
forall a. DList a
DList.empty
    }

{- |
  Adds a parameter value to the end of the params list, tracking the count
  of parameters as it does so.

@since 1.0.0.0
-}
snocParam :: ParamsProgress -> Maybe PgTextFormatValue -> ParamsProgress
snocParam :: ParamsProgress -> Maybe PgTextFormatValue -> ParamsProgress
snocParam (ParamsProgress Int
count DList (Maybe PgTextFormatValue)
values) Maybe PgTextFormatValue
newValue =
  ParamsProgress
    { paramCount :: Int
paramCount = Int
count Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
    , paramValues :: DList (Maybe PgTextFormatValue)
paramValues = DList (Maybe PgTextFormatValue)
-> Maybe PgTextFormatValue -> DList (Maybe PgTextFormatValue)
forall a. DList a -> a -> DList a
DList.snoc DList (Maybe PgTextFormatValue)
values Maybe PgTextFormatValue
newValue
    }

{- |
  Constructs a bytestring builder that can be executed to get the bytes for a
  section of 'RawSql'. This function takes and returns a 'ParamsProgress' so
  that placeholder indices (e.g. '$1') and their corresponding parameter values
  can be tracked across multiple sections of raw SQL.

@since 1.0.0.0
-}
buildSqlWithProgress ::
  Monad m =>
  Quoting m ->
  ParamsProgress ->
  RawSql ->
  m (BSB.Builder, ParamsProgress)
buildSqlWithProgress :: forall (m :: * -> *).
Monad m =>
Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
buildSqlWithProgress Quoting m
quoting ParamsProgress
progress RawSql
rawSql =
  case RawSql
rawSql of
    SqlSection Builder
builder ->
      (Builder, ParamsProgress) -> m (Builder, ParamsProgress)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
builder, ParamsProgress
progress)
    StringLiteral ByteString
unquotedString -> do
      Builder
quotedString <- Quoting m -> ByteString -> m Builder
forall (m :: * -> *). Quoting m -> ByteString -> m Builder
quoteStringLiteral Quoting m
quoting ByteString
unquotedString
      (Builder, ParamsProgress) -> m (Builder, ParamsProgress)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
quotedString, ParamsProgress
progress)
    Identifier ByteString
unquotedIdentifier -> do
      Builder
quotedIdentifier <- Quoting m -> ByteString -> m Builder
forall (m :: * -> *). Quoting m -> ByteString -> m Builder
quoteIdentifier Quoting m
quoting ByteString
unquotedIdentifier
      (Builder, ParamsProgress) -> m (Builder, ParamsProgress)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
quotedIdentifier, ParamsProgress
progress)
    Parameter SqlValue
value ->
      let
        newProgress :: ParamsProgress
newProgress = ParamsProgress -> Maybe PgTextFormatValue -> ParamsProgress
snocParam ParamsProgress
progress (SqlValue -> Maybe PgTextFormatValue
SqlValue.toPgValue SqlValue
value)
        placeholder :: Builder
placeholder = String -> Builder
BSB.stringUtf8 String
"$" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Builder
BSB.intDec (ParamsProgress -> Int
paramCount ParamsProgress
newProgress)
      in
        (Builder, ParamsProgress) -> m (Builder, ParamsProgress)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
placeholder, ParamsProgress
newProgress)
    Append RawSql
first RawSql
second -> do
      (Builder
firstBuilder, ParamsProgress
nextProgress) <- Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
forall (m :: * -> *).
Monad m =>
Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
buildSqlWithProgress Quoting m
quoting ParamsProgress
progress RawSql
first
      (Builder
secondBuilder, ParamsProgress
finalProgress) <- Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
forall (m :: * -> *).
Monad m =>
Quoting m
-> ParamsProgress -> RawSql -> m (Builder, ParamsProgress)
buildSqlWithProgress Quoting m
quoting ParamsProgress
nextProgress RawSql
second
      (Builder, ParamsProgress) -> m (Builder, ParamsProgress)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
firstBuilder Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
secondBuilder, ParamsProgress
finalProgress)

{- |
  Constructs a 'RawSql' from a 'String' value using UTF-8 encoding.

  Note that because the string is treated as raw SQL, it is completely up to
  the caller to protected againt SQL-injection attacks when using this
  function. Never use this function with input read from an untrusted source.

@since 1.0.0.0
-}
fromString :: String -> RawSql
fromString :: String -> RawSql
fromString =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (String -> Builder) -> String -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Builder
BSB.stringUtf8

{- |
  Constructs a 'RawSql' from a 'T.Text' value using UTF-8 encoding.

  Note that because the text is treated as raw SQL, it is completely up to the
  caller to protected againt SQL-injection attacks when using this function.
  Never use this function with input read from an untrusted source.

@since 1.0.0.0
-}
fromText :: T.Text -> RawSql
fromText :: Text -> RawSql
fromText =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Text -> Builder) -> Text -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
TextEnc.encodeUtf8Builder

{- |
  Constructs a 'RawSql' from a 'BS.ByteString' value, which is assumed to be
  encoded sensibly for the database to handle.

  Note that because the string is treated as raw SQL, it is completely up to
  the caller to protected againt SQL-injection attacks when using this
  function. Never use this function with input read from an untrusted source.

@since 1.0.0.0
-}
fromBytes :: BS.ByteString -> RawSql
fromBytes :: ByteString -> RawSql
fromBytes =
  Builder -> RawSql
SqlSection (Builder -> RawSql)
-> (ByteString -> Builder) -> ByteString -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Builder
BSB.byteString

{- |
  Includes an input parameter in the 'RawSql' statement that will be passed
  using placeholders (e.g. '$1') rather than being included directly in the SQL
  statement. This is the correct way to include input from untrusted sources as
  part of a 'RawSql' query. The parameter must be formatted in a textual
  representation, which the database will interpret. The database type for the
  value will be inferred by the database based on its usage in the query.

@since 1.0.0.0
-}
parameter :: SqlValue -> RawSql
parameter :: SqlValue -> RawSql
parameter =
  SqlValue -> RawSql
Parameter

{- |
  Includes a bytestring value as a string literal in the SQL statement. The
  string literal will be quoted and escaped for you; the value provided should
  not include surrounding quotes or quote special characters.

  Note: It's better to use the 'parameter' function where possible to pass
  values to be used as input to a SQL statement. There are some situations
  where PostgreSQL does not allow this, however (for instance, in some DDL
  statements). This function is provided for those situations.

@since 1.0.0.0
-}
stringLiteral :: BS.ByteString -> RawSql
stringLiteral :: ByteString -> RawSql
stringLiteral =
  ByteString -> RawSql
StringLiteral

{- |
  Includes a bytestring value as an identifier in the SQL statement. The
  identifier will be quoted and escaped for you; the value provided should not
  include surrounding quotes or quote special characters.

@since 1.0.0.0
-}
identifier :: BS.ByteString -> RawSql
identifier :: ByteString -> RawSql
identifier =
  ByteString -> RawSql
Identifier

{- |
  Concatenates a list of 'RawSql' values using another 'RawSql' value as the
  separator between the items.

@since 1.0.0.0
-}
intercalate :: (SqlExpression sql, Foldable f) => RawSql -> f sql -> RawSql
intercalate :: forall sql (f :: * -> *).
(SqlExpression sql, Foldable f) =>
RawSql -> f sql -> RawSql
intercalate RawSql
separator =
  [RawSql] -> RawSql
forall a. Monoid a => [a] -> a
mconcat
    ([RawSql] -> RawSql) -> (f sql -> [RawSql]) -> f sql -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RawSql -> [RawSql] -> [RawSql]
forall a. a -> [a] -> [a]
List.intersperse RawSql
separator
    ([RawSql] -> [RawSql]) -> (f sql -> [RawSql]) -> f sql -> [RawSql]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (sql -> RawSql) -> [sql] -> [RawSql]
forall a b. (a -> b) -> [a] -> [b]
map sql -> RawSql
forall a. SqlExpression a => a -> RawSql
toRawSql
    ([sql] -> [RawSql]) -> (f sql -> [sql]) -> f sql -> [RawSql]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. f sql -> [sql]
forall a. f a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
Fold.toList

{- |
  Executes a 'RawSql' value using the 'Conn.executeRaw' function. Make sure
  to read the documentation of 'Conn.executeRaw' for caveats and warnings.
  Use with caution.

  Note that because this is done in 'IO', no callback functions are available
  to be called.

@since 1.0.0.0
-}
execute :: SqlExpression sql => Conn.Connection -> sql -> IO LibPQ.Result
execute :: forall sql. SqlExpression sql => Connection -> sql -> IO Result
execute Connection
connection sql
sql = do
  (ByteString
sqlBytes, [Maybe PgTextFormatValue]
params) <- Quoting IO -> sql -> IO (ByteString, [Maybe PgTextFormatValue])
forall sql (m :: * -> *).
(SqlExpression sql, Monad m) =>
Quoting m -> sql -> m (ByteString, [Maybe PgTextFormatValue])
toBytesAndParams (Connection -> Quoting IO
connectionQuoting Connection
connection) sql
sql
  Connection -> ByteString -> [Maybe PgTextFormatValue] -> IO Result
Conn.executeRaw Connection
connection ByteString
sqlBytes [Maybe PgTextFormatValue]
params

{- |
  Executes a 'RawSql' value using the 'Conn.executeRawVoid' function. Make sure
  to read the documentation of 'Conn.executeRawVoid' for caveats and warnings.
  Use with caution.

  Note that because this is done in 'IO', no callback functions are available
  to be called.

@since 1.0.0.0
-}
executeVoid :: SqlExpression sql => Conn.Connection -> sql -> IO ()
executeVoid :: forall sql. SqlExpression sql => Connection -> sql -> IO ()
executeVoid Connection
connection sql
sql = do
  IO Result -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Result -> IO ()) -> IO Result -> IO ()
forall a b. (a -> b) -> a -> b
$ Connection -> sql -> IO Result
forall sql. SqlExpression sql => Connection -> sql -> IO Result
execute Connection
connection sql
sql

-- | Just a plain old space, provided for convenience.
space :: RawSql
space :: RawSql
space = String -> RawSql
fromString String
" "

-- | Just a plain old comma, provided for convenience.
comma :: RawSql
comma :: RawSql
comma = String -> RawSql
fromString String
","

-- | Comma space separator, provided for convenience.
commaSpace :: RawSql
commaSpace :: RawSql
commaSpace = String -> RawSql
fromString String
", "

-- | Just a plain old left paren, provided for convenience.
leftParen :: RawSql
leftParen :: RawSql
leftParen = String -> RawSql
fromString String
"("

-- | Just a plain old right paren, provided for convenience.
rightParen :: RawSql
rightParen :: RawSql
rightParen = String -> RawSql
fromString String
")"

-- | Just a plain period, provided for convenience.
dot :: RawSql
dot :: RawSql
dot = String -> RawSql
fromString String
"."

-- | Just a plain double quote, provided for convenience.
doubleQuote :: RawSql
doubleQuote :: RawSql
doubleQuote = String -> RawSql
fromString String
"\""

-- | Just two colons, provided for convenience.
doubleColon :: RawSql
doubleColon :: RawSql
doubleColon = String -> RawSql
fromString String
"::"

{- |
  Constructs a 'RawSql' from an 'Int.Int8' value. The integral value is
  included directly in the SQL string, not passed as a parameter. When dealing
  with user input, it is better to use 'parameter' whenever possible.

@since 1.0.0.0
-}
int8DecLiteral :: Int.Int8 -> RawSql
int8DecLiteral :: Int8 -> RawSql
int8DecLiteral =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Int8 -> Builder) -> Int8 -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int8 -> Builder
BSB.int8Dec

{- |
  Constructs a 'RawSql' from an 'Int.Int16' value. The integral value is
  included directly in the SQL string, not passed as a parameter. When dealing
  with user input, it is better to use 'parameter' whenever possible.

@since 1.0.0.0
-}
int16DecLiteral :: Int.Int16 -> RawSql
int16DecLiteral :: Int16 -> RawSql
int16DecLiteral =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Int16 -> Builder) -> Int16 -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int16 -> Builder
BSB.int16Dec

{- |
  Constructs a 'RawSql' from an 'Int.Int32' value. The integral value is
  included directly in the SQL string, not passed as a parameter. When dealing
  with user input, it is better to use 'parameter' whenever possible.

@since 1.0.0.0
-}
int32DecLiteral :: Int.Int32 -> RawSql
int32DecLiteral :: Int32 -> RawSql
int32DecLiteral =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Int32 -> Builder) -> Int32 -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> Builder
BSB.int32Dec

{- |
  Constructs a 'RawSql' from an 'Int.Int64' value. The integral value is
  included directly in the SQL string, not passed as a parameter. When dealing
  with user input, it is better to use 'parameter' whenever possible.

@since 1.0.0.0
-}
int64DecLiteral :: Int.Int64 -> RawSql
int64DecLiteral :: Int64 -> RawSql
int64DecLiteral =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Int64 -> Builder) -> Int64 -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Builder
BSB.int64Dec

{- |
  Constructs a 'RawSql' from an 'Int' value. The integral value is included
  directly in the SQL string, not passed as a parameter. When dealing with user
  input, it is better to use 'parameter' whenever possible.

@since 1.0.0.0
-}
intDecLiteral :: Int -> RawSql
intDecLiteral :: Int -> RawSql
intDecLiteral =
  Builder -> RawSql
SqlSection (Builder -> RawSql) -> (Int -> Builder) -> Int -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Builder
BSB.intDec

{- |
  Constructs a 'RawSql' by putting parentheses around an arbitrary expression.
  The result is returned as a 'RawSql'. It is up to the caller to decide
  whether it should be wrapped in a more-specific expression type.

@since 1.0.0.0
-}
parenthesized :: SqlExpression sql => sql -> RawSql
parenthesized :: forall a. SqlExpression a => a -> RawSql
parenthesized sql
expr =
  RawSql
leftParen RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> sql -> RawSql
forall a. SqlExpression a => a -> RawSql
toRawSql sql
expr RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
rightParen