packages feed

orville-postgresql-1.1.0.0: src/Orville/PostgreSQL/Expr/Function.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

{- |
Copyright : Flipstone Technology Partners 2024
License   : MIT
Stability : Stable

@since 1.1.0.0
-}
module Orville.PostgreSQL.Expr.Function
  ( DropFunctionExpr
  , dropFunction
  , CreateFunctionExpr
  , createFunction
  , FunctionReturns
  , returns
  , ReturnType
  , returnTypeTrigger
  , FunctionLanguage
  , language
  , LanguageName
  , plpgsql
  , FunctionDefinition
  , asDefinition
  ) where

import qualified Data.ByteString.Char8 as BS8
import Data.Maybe (catMaybes)

import Orville.PostgreSQL.Expr.IfExists (IfExists)
import Orville.PostgreSQL.Expr.Name (FunctionName, QualifiedOrUnqualified)
import Orville.PostgreSQL.Expr.OrReplace (OrReplace)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql

{- | Type to represent a SQL "DROP FUNCTION" statement. E.G.

> DROP FUNCTION my_function

'DropFunctionExpr' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype DropFunctionExpr
  = DropFunctionExpr RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | Constructs a SQL @DROP FUNCTION@ statement from a function name.

@since 1.1.0.0
-}
dropFunction :: Maybe IfExists -> QualifiedOrUnqualified FunctionName -> DropFunctionExpr
dropFunction maybeIfExists name =
  DropFunctionExpr $
    RawSql.intercalate
      RawSql.space
      ( catMaybes
          [ Just (RawSql.fromString "DROP FUNCTION")
          , fmap RawSql.toRawSql maybeIfExists
          , Just (RawSql.toRawSql name)
          ]
      )

{- | Type to represent a SQL "CREATE FUNCTION" statement. E.G.

> CREATE FUNCTION my_function RETURNS trigger AS '<definition>'

'CreateFunctionExpr' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype CreateFunctionExpr
  = CreateFunctionExpr RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | Constructs a SQL @CREATE FUNCTION@ statement from a function name, return type,
language and definiton.

Note: Orville does not currently support creating functions with arguments.

@since 1.1.0.0
-}
createFunction ::
  Maybe OrReplace ->
  QualifiedOrUnqualified FunctionName ->
  FunctionReturns ->
  FunctionLanguage ->
  FunctionDefinition ->
  CreateFunctionExpr
createFunction maybeOrReplace name functionReturns functionLanguage definition =
  CreateFunctionExpr $
    RawSql.intercalate
      RawSql.space
      ( catMaybes
          [ Just $ RawSql.fromString "CREATE"
          , fmap RawSql.toRawSql maybeOrReplace
          , Just $ RawSql.fromString "FUNCTION"
          , Just $ RawSql.toRawSql name
          , Just $ RawSql.fromString "()" -- currently we don't support specifying arguments
          , Just $ RawSql.toRawSql functionReturns
          , Just $ RawSql.toRawSql functionLanguage
          , Just $ RawSql.toRawSql definition
          ]
      )

{- | Type to represent the return specifier given as part of a SQL "CREATE
FUNCTION" statement. E.G. the @RETURNS trigger@ in

> CREATE FUNCTION my_function RETURNS trigger

'FunctionReturns' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype FunctionReturns
  = FunctionReturns RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | Constructs a @RETURNS@ specifier for a @CREATE FUNCTION@ statement with the
  given return type.

@since 1.1.0.0
-}
returns :: ReturnType -> FunctionReturns
returns returnType =
  FunctionReturns $ RawSql.fromString "RETURNS " <> RawSql.toRawSql returnType

{- | Type to represent the return type given as part of a SQL "CREATE
FUNCTION" statement. E.G. the @trigger@ in

> CREATE FUNCTION my_function RETURNS trigger

'ReturnType' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype ReturnType
  = ReturnType RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | The @trigger@ return type.

@since 1.1.0.0
-}
returnTypeTrigger :: ReturnType
returnTypeTrigger =
  ReturnType (RawSql.fromString "trigger")

{- | Type to represent the language specifier given as part of a SQL "CREATE
FUNCTION" statement. E.G. the @LANGUAGE plpgsql@ in

> CREATE FUNCTION my_function LANGUAGE plpgsql

'FunctionLanguage' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype FunctionLanguage
  = FunctionLanguage RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | Constructs a @LANGUAGE@ specifier for a @CREATE FUNCTION@ statement with the
  given language name as the language.

@since 1.1.0.0
-}
language :: LanguageName -> FunctionLanguage
language name =
  FunctionLanguage $ RawSql.fromString "LANGUAGE " <> RawSql.toRawSql name

{- | Type to represent the language that the function definition is given in as part
of a SQL "CREATE FUNCTION" statement. E.G. the @plpgsql@ in

> CREATE FUNCTION my_function LANGUAGE plpgsql

'LanguageName' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype LanguageName
  = LanguageName RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | The @plpgsql@ language.

@since 1.1.0.0
-}
plpgsql :: LanguageName
plpgsql =
  LanguageName (RawSql.fromString "plpgsql")

{- | Type to represent the definition body of a a SQL "CREATE FUNCTION" statement. E.G. the
@AS <definition>@ in

> CREATE FUNCTION my_function AS <definition>

'FunctionDefinition' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.1.0.0
-}
newtype FunctionDefinition
  = FunctionDefinition RawSql.RawSql
  deriving
    ( -- | @since 1.1.0.0
      RawSql.SqlExpression
    )

{- | Constructs a 'FunctionDefinition' from a 'String', which will be passed as an
  escaped string literal to PosgreSQL.

@since 1.1.0.0
-}
asDefinition :: String -> FunctionDefinition
asDefinition body =
  FunctionDefinition $
    RawSql.fromString "AS " <> RawSql.stringLiteral (BS8.pack body)