module Database.PostgreSQL.Query.SqlBuilder.Builder
       ( SqlBuilder(..)
         -- * Running
       , runSqlBuilder
         -- * Building
       , emptyB
       , mkValue
       , mkMaskedValue
       , sqlBuilderFromField
       -- ** Unsafe
       , sqlBuilderPure
       , sqlBuilderFromByteString
       ) where

import Blaze.ByteString.Builder (Builder)
import Data.ByteString (ByteString)
import Data.String
import Data.Typeable
import Database.PostgreSQL.Query.Import
import Database.PostgreSQL.Query.SqlBuilder.Types
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.Internal
import Database.PostgreSQL.Simple.ToField
import Database.PostgreSQL.Simple.Types

import qualified Blaze.ByteString.Builder as BB
import qualified Blaze.ByteString.Builder.Char.Utf8 as BB


-- | Builder wich can be effectively concatenated. Requires 'Connection'
-- inside for string quoting implemented in __libpq__. Builds two strings: query
-- string and log string which may differ.
newtype SqlBuilder = SqlBuilder
  { SqlBuilder -> Connection -> LogMasker -> IO SqlBuilderResult
sqlBuild :: Connection -> LogMasker -> IO SqlBuilderResult
  } deriving (Typeable, (forall x. SqlBuilder -> Rep SqlBuilder x)
-> (forall x. Rep SqlBuilder x -> SqlBuilder) -> Generic SqlBuilder
forall x. Rep SqlBuilder x -> SqlBuilder
forall x. SqlBuilder -> Rep SqlBuilder x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SqlBuilder x -> SqlBuilder
$cfrom :: forall x. SqlBuilder -> Rep SqlBuilder x
Generic)

instance Semigroup SqlBuilder where
  (SqlBuilder Connection -> LogMasker -> IO SqlBuilderResult
a) <> :: SqlBuilder -> SqlBuilder -> SqlBuilder
<> (SqlBuilder Connection -> LogMasker -> IO SqlBuilderResult
b) =
    (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
SqlBuilder ((Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder)
-> (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
forall a b. (a -> b) -> a -> b
$ \Connection
c LogMasker
masker -> SqlBuilderResult -> SqlBuilderResult -> SqlBuilderResult
forall a. Semigroup a => a -> a -> a
(<>) (SqlBuilderResult -> SqlBuilderResult -> SqlBuilderResult)
-> IO SqlBuilderResult -> IO (SqlBuilderResult -> SqlBuilderResult)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Connection -> LogMasker -> IO SqlBuilderResult
a Connection
c LogMasker
masker) IO (SqlBuilderResult -> SqlBuilderResult)
-> IO SqlBuilderResult -> IO SqlBuilderResult
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Connection -> LogMasker -> IO SqlBuilderResult
b Connection
c LogMasker
masker)

instance Monoid SqlBuilder where
  mempty :: SqlBuilder
mempty = Builder -> SqlBuilder
sqlBuilderPure Builder
forall a. Monoid a => a
mempty
  mappend :: SqlBuilder -> SqlBuilder -> SqlBuilder
mappend = SqlBuilder -> SqlBuilder -> SqlBuilder
forall a. Semigroup a => a -> a -> a
(<>)

instance IsString SqlBuilder where
  fromString :: String -> SqlBuilder
fromString String
s = (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
SqlBuilder ((Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder)
-> (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
forall a b. (a -> b) -> a -> b
$ \Connection
_ LogMasker
_ -> SqlBuilderResult -> IO SqlBuilderResult
forall (m :: * -> *) a. Monad m => a -> m a
return (SqlBuilderResult -> IO SqlBuilderResult)
-> SqlBuilderResult -> IO SqlBuilderResult
forall a b. (a -> b) -> a -> b
$ Builder -> SqlBuilderResult
builderResultPure (Builder -> SqlBuilderResult) -> Builder -> SqlBuilderResult
forall a b. (a -> b) -> a -> b
$ String -> Builder
BB.fromString String
s

-- | Returns query string with log bytestring
runSqlBuilder :: Connection -> LogMasker -> SqlBuilder -> IO (Query, ByteString)
runSqlBuilder :: Connection -> LogMasker -> SqlBuilder -> IO (Query, ByteString)
runSqlBuilder Connection
con LogMasker
masker (SqlBuilder Connection -> LogMasker -> IO SqlBuilderResult
bld) = SqlBuilderResult -> (Query, ByteString)
toTuple (SqlBuilderResult -> (Query, ByteString))
-> IO SqlBuilderResult -> IO (Query, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Connection -> LogMasker -> IO SqlBuilderResult
bld Connection
con LogMasker
masker
  where
    toTuple :: SqlBuilderResult -> (Query, ByteString)
toTuple SqlBuilderResult
res =
      ( ByteString -> Query
Query (ByteString -> Query) -> ByteString -> Query
forall a b. (a -> b) -> a -> b
$ Builder -> ByteString
BB.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ SqlBuilderResult -> Builder
sbQueryString SqlBuilderResult
res
      , Builder -> ByteString
BB.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ SqlBuilderResult -> Builder
sbLogString SqlBuilderResult
res )

-- | Typed synonym of 'mempty'
emptyB :: SqlBuilder
emptyB :: SqlBuilder
emptyB = SqlBuilder
forall a. Monoid a => a
mempty

-- | Shorthand function to convert single field value to builder
mkValue :: (ToField a) => a -> SqlBuilder
mkValue :: a -> SqlBuilder
mkValue = FieldOption -> a -> SqlBuilder
forall a. ToField a => FieldOption -> a -> SqlBuilder
sqlBuilderFromField FieldOption
FieldDefault

-- | Shorthand function to convert single masked field value (which should not
-- be shown in log)
mkMaskedValue :: (ToField a) => a -> SqlBuilder
mkMaskedValue :: a -> SqlBuilder
mkMaskedValue = FieldOption -> a -> SqlBuilder
forall a. ToField a => FieldOption -> a -> SqlBuilder
sqlBuilderFromField FieldOption
FieldMasked

sqlBuilderFromField :: (ToField a) => FieldOption -> a -> SqlBuilder
sqlBuilderFromField :: FieldOption -> a -> SqlBuilder
sqlBuilderFromField FieldOption
fo a
field = (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
SqlBuilder ((Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder)
-> (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
forall a b. (a -> b) -> a -> b
$ \Connection
con LogMasker
masker -> do
  Builder
qbs <- Connection -> Query -> [Action] -> Action -> IO Builder
buildAction Connection
con Query
"" [] (Action -> IO Builder) -> Action -> IO Builder
forall a b. (a -> b) -> a -> b
$ a -> Action
forall a. ToField a => a -> Action
toField a
field
  let sbQueryString :: Builder
sbQueryString = Builder
qbs
      sbLogString :: Builder
sbLogString   = LogMasker
masker FieldOption
fo Builder
qbs
  SqlBuilderResult -> IO SqlBuilderResult
forall (m :: * -> *) a. Monad m => a -> m a
return SqlBuilderResult :: Builder -> Builder -> SqlBuilderResult
SqlBuilderResult{Builder
sbLogString :: Builder
sbQueryString :: Builder
sbLogString :: Builder
sbQueryString :: Builder
..}

-- | Lift pure bytestring builder to 'SqlBuilder'. This is unsafe to use
-- directly in your code.
sqlBuilderPure :: Builder -> SqlBuilder
sqlBuilderPure :: Builder -> SqlBuilder
sqlBuilderPure Builder
b = (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
SqlBuilder ((Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder)
-> (Connection -> LogMasker -> IO SqlBuilderResult) -> SqlBuilder
forall a b. (a -> b) -> a -> b
$ \Connection
_ LogMasker
_ -> SqlBuilderResult -> IO SqlBuilderResult
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SqlBuilderResult -> IO SqlBuilderResult)
-> SqlBuilderResult -> IO SqlBuilderResult
forall a b. (a -> b) -> a -> b
$ Builder -> SqlBuilderResult
builderResultPure Builder
b

-- | Unsafe function to make SqlBuilder from arbitrary ByteString. Does not
-- perform any checks. Dont use it directly in your code unless you know what
-- you are doing.
sqlBuilderFromByteString :: ByteString -> SqlBuilder
sqlBuilderFromByteString :: ByteString -> SqlBuilder
sqlBuilderFromByteString = Builder -> SqlBuilder
sqlBuilderPure (Builder -> SqlBuilder)
-> (ByteString -> Builder) -> ByteString -> SqlBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Builder
BB.fromByteString