orville-postgresql-1.0.0.0: src/Orville/PostgreSQL/Marshall/SyntheticField.hs
{-# LANGUAGE OverloadedStrings #-}
{- |
Copyright : Flipstone Technology Partners 2023
License : MIT
Stability : Stable
@since 1.0.0.0
-}
module Orville.PostgreSQL.Marshall.SyntheticField
( SyntheticField
, syntheticFieldExpression
, syntheticFieldAlias
, syntheticFieldValueFromSqlValue
, syntheticField
, nullableSyntheticField
, prefixSyntheticField
)
where
import qualified Data.ByteString.Char8 as B8
import qualified Orville.PostgreSQL.Expr as Expr
import Orville.PostgreSQL.Marshall.FieldDefinition (FieldName, byteStringToFieldName, fieldNameToByteString, stringToFieldName)
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue
{- |
A 'SyntheticField' can be used to evaluate a SQL expression based on the
columns of a table when records are selected from the database. Synthetic
fields are inherently read-only.
@since 1.0.0.0
-}
data SyntheticField a = SyntheticField
{ _syntheticFieldExpression :: Expr.ValueExpression
, _syntheticFieldAlias :: FieldName
, _syntheticFieldValueFromSqlValue :: SqlValue.SqlValue -> Either String a
}
{- |
Returns the SQL expression that should be used in select statements to
calculate the synthetic field.
@since 1.0.0.0
-}
syntheticFieldExpression :: SyntheticField a -> Expr.ValueExpression
syntheticFieldExpression =
_syntheticFieldExpression
{- |
Returns the alias that should be used in select statements to name the
synthetic field.
@since 1.0.0.0
-}
syntheticFieldAlias :: SyntheticField a -> FieldName
syntheticFieldAlias =
_syntheticFieldAlias
{- |
Decodes a calculated value selected from the database to its expected
Haskell type. Returns a 'Left' with an error message if the decoding fails.
@since 1.0.0.0
-}
syntheticFieldValueFromSqlValue :: SyntheticField a -> SqlValue.SqlValue -> Either String a
syntheticFieldValueFromSqlValue =
_syntheticFieldValueFromSqlValue
{- |
Constructs a 'SyntheticField' that will select a SQL expression using
the given alias.
@since 1.0.0.0
-}
syntheticField ::
-- | The SQL expression to be selected.
Expr.ValueExpression ->
-- | The alias to be used to name the calculation in SQL expressions.
String ->
-- | A function to decode the expression result from a 'SqlValue.SqlValue'.
(SqlValue.SqlValue -> Either String a) ->
SyntheticField a
syntheticField expression alias fromSqlValue =
SyntheticField
{ _syntheticFieldExpression = expression
, _syntheticFieldAlias = stringToFieldName alias
, _syntheticFieldValueFromSqlValue = fromSqlValue
}
{- |
Modifies a 'SyntheticField' to allow it to decode @NULL@ values.
@since 1.0.0.0
-}
nullableSyntheticField :: SyntheticField a -> SyntheticField (Maybe a)
nullableSyntheticField synthField =
synthField
{ _syntheticFieldValueFromSqlValue = \sqlValue ->
if SqlValue.isSqlNull sqlValue
then Right Nothing
else Just <$> syntheticFieldValueFromSqlValue synthField sqlValue
}
{- |
Adds a prefix, followed by an underscore, to the alias used to name the
synthetic field.
@since 1.0.0.0
-}
prefixSyntheticField ::
String ->
SyntheticField a ->
SyntheticField a
prefixSyntheticField prefix synthField =
synthField
{ _syntheticFieldAlias = byteStringToFieldName (B8.pack prefix <> "_" <> fieldNameToByteString (syntheticFieldAlias synthField))
}