packages feed

orville-postgresql-1.0.0.0: src/Orville/PostgreSQL/Expr/SequenceDefinition.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.SequenceDefinition
  ( CreateSequenceExpr
  , createSequenceExpr
  , AlterSequenceExpr
  , alterSequenceExpr
  , IncrementByExpr
  , incrementBy
  , MinValueExpr
  , minValue
  , noMinValue
  , MaxValueExpr
  , maxValue
  , noMaxValue
  , StartWithExpr
  , startWith
  , CacheExpr
  , cache
  , CycleExpr
  , cycle
  , noCycle
  , cycleIfTrue
  , DropSequenceExpr
  , dropSequenceExpr
  , nextVal
  , nextValFunction
  , currVal
  , currValFunction
  , setVal
  , setValFunction
  )
where

-- to avoid conflict with cycle
import Prelude (Bool, Maybe (Just), fmap, ($), (.), (<>))

import Data.Int (Int64)
import Data.Maybe (catMaybes)

import Orville.PostgreSQL.Expr.IfExists (IfExists)
import Orville.PostgreSQL.Expr.Name (FunctionName, Qualified, SequenceName, functionName)
import Orville.PostgreSQL.Expr.ValueExpression (ValueExpression, functionCall, valueExpression)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue

{-
   From https://www.postgresql.org/docs/15/sql-createsequence.html

   @@
   CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] SEQUENCE [ IF NOT EXISTS ] name
    [ AS data_type ]
    [ INCREMENT [ BY ] increment ]
    [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
    [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { table_name.column_name | NONE } ]
   @@
-}

{- |
Type to represent a @CREATE SEQUENCE@ statement. E.G.

> CREATE SEQUENCE foo INCREMENT 2

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

@since 1.0.0.0
-}
newtype CreateSequenceExpr
  = CreateSequenceExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'CreateSequenceExpr' with the given sequence options.

  @since 1.0.0.0
-}
createSequenceExpr ::
  -- | The name to be used for the sequence.
  Qualified SequenceName ->
  -- | An optional @INCREMENT@ expression.
  Maybe IncrementByExpr ->
  -- | An optional @MINVALUE@ expression.
  Maybe MinValueExpr ->
  -- | An optional @MAXVALUE@ expression.
  Maybe MaxValueExpr ->
  -- | An optional @START WITH@ expression.
  Maybe StartWithExpr ->
  -- | An optional @CACHE@ expression.
  Maybe CacheExpr ->
  -- | An optional @CYCLE@ expression.
  Maybe CycleExpr ->
  CreateSequenceExpr
createSequenceExpr sequenceName mbIncrementBy mbMinValue mbMaxValue mbStartWith mbCache mbCycle =
  CreateSequenceExpr
    . RawSql.intercalate RawSql.space
    . catMaybes
    $ [ Just (RawSql.fromString "CREATE SEQUENCE")
      , Just (RawSql.toRawSql sequenceName)
      , fmap RawSql.toRawSql mbIncrementBy
      , fmap RawSql.toRawSql mbMinValue
      , fmap RawSql.toRawSql mbMaxValue
      , fmap RawSql.toRawSql mbStartWith
      , fmap RawSql.toRawSql mbCache
      , fmap RawSql.toRawSql mbCycle
      ]

{-
  From https://www.postgresql.org/docs/15/sql-altersequence.html

  @@
  ALTER SEQUENCE [ IF EXISTS ] name
    [ AS data_type ]
    [ INCREMENT [ BY ] increment ]
    [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
    [ START [ WITH ] start ]
    [ RESTART [ [ WITH ] restart ] ]
    [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { table_name.column_name | NONE } ]
  ALTER SEQUENCE [ IF EXISTS ] name SET { LOGGED | UNLOGGED }
  ALTER SEQUENCE [ IF EXISTS ] name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
  ALTER SEQUENCE [ IF EXISTS ] name RENAME TO new_name
  ALTER SEQUENCE [ IF EXISTS ] name SET SCHEMA new_schema
  @@
-}

{- |
Type to represent a @CREATE SEQUENCE@ statement. E.G.

> ALTER SEQUENCE foo START WITH 0

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

@since 1.0.0.0
-}
newtype AlterSequenceExpr
  = AlterSequenceExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs an 'AlterSequenceExpr' with the given sequence options.

  @since 1.0.0.0
-}
alterSequenceExpr ::
  -- | The name of the sequence to alter
  Qualified SequenceName ->
  -- | An optional @INCREMENT@ expression
  Maybe IncrementByExpr ->
  -- | An optional @MINVALUE@ expression
  Maybe MinValueExpr ->
  -- | An optional @MAXVALUE@ expression
  Maybe MaxValueExpr ->
  -- | An optional @START WITH@ expression
  Maybe StartWithExpr ->
  -- | An optional @CACHE@ expression
  Maybe CacheExpr ->
  -- | An optional @CYCLE@ expression
  Maybe CycleExpr ->
  AlterSequenceExpr
alterSequenceExpr sequenceName mbIncrementBy mbMinValue mbMaxValue mbStartWith mbCache mbCycle =
  AlterSequenceExpr
    . RawSql.intercalate RawSql.space
    . catMaybes
    $ [ Just (RawSql.fromString "ALTER SEQUENCE")
      , Just (RawSql.toRawSql sequenceName)
      , fmap RawSql.toRawSql mbIncrementBy
      , fmap RawSql.toRawSql mbMinValue
      , fmap RawSql.toRawSql mbMaxValue
      , fmap RawSql.toRawSql mbStartWith
      , fmap RawSql.toRawSql mbCache
      , fmap RawSql.toRawSql mbCycle
      ]

{- |
Type to represent an @INCREMENT BY@ expression for sequences. E.G.

> INCREMENT BY 0

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

@since 1.0.0.0
-}
newtype IncrementByExpr
  = IncrementByExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs an 'IncrementByExpr' that will make the sequence increment by
  the given value.

  @since 1.0.0.0
-}
incrementBy :: Int64 -> IncrementByExpr
incrementBy n =
  IncrementByExpr $
    RawSql.fromString "INCREMENT BY "
      <> RawSql.int64DecLiteral n

{- |
Type to represent a @MINVALUE@ expression for sequences. E.G.

> MINVALUE 0

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

@since 1.0.0.0
-}
newtype MinValueExpr
  = MinValueExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'MinValueExpr' which gives the sequence the specified minimum
  value.

  @since 1.0.0.0
-}
minValue :: Int64 -> MinValueExpr
minValue n =
  MinValueExpr $
    RawSql.fromString "MINVALUE "
      <> RawSql.int64DecLiteral n

{- |
  Constructs a 'MinValueExpr' which gives the sequence no minimum value (i.e.
  @NO MINVALUE@).

  @since 1.0.0.0
-}
noMinValue :: MinValueExpr
noMinValue =
  MinValueExpr . RawSql.fromString $ "NO MINVALUE"

{- |
Type to represent a @MAXVALUE@ expression for sequences. E.G.

> MAXVALUE 1000000

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

@since 1.0.0.0
-}
newtype MaxValueExpr
  = MaxValueExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'MaxValueExpr' which gives the sequence the specified maximum
  value.

  @since 1.0.0.0
-}
maxValue :: Int64 -> MaxValueExpr
maxValue n =
  MaxValueExpr $
    RawSql.fromString "MAXVALUE "
      <> RawSql.int64DecLiteral n

{- |
  Constructs a 'MaxValueExpr' which gives the sequence no maximum value (i.e.
  @NO MAXVALUE@).

  @since 1.0.0.0
-}
noMaxValue :: MaxValueExpr
noMaxValue =
  MaxValueExpr . RawSql.fromString $ "NO MAXVALUE"

{- |
Type to represent a @START WITH@ expression for sequences. E.G.

> START WITH 1

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

@since 1.0.0.0
-}
newtype StartWithExpr
  = StartWithExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'StartWithExpr' which gives the sequence the specified start
  value.

  @since 1.0.0.0
-}
startWith :: Int64 -> StartWithExpr
startWith n =
  StartWithExpr $
    RawSql.fromString "START WITH "
      <> RawSql.int64DecLiteral n

{- |
Type to represent a @CACHE@ expression for sequences. E.G.

> CACHE 16

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

@since 1.0.0.0
-}
newtype CacheExpr
  = CacheExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'CacheExpr' that will make the sequence pre-allocate the
  specified number of sequence values.

  @since 1.0.0.0
-}
cache :: Int64 -> CacheExpr
cache n =
  CacheExpr $
    RawSql.fromString "CACHE "
      <> RawSql.int64DecLiteral n

{- |
Type to represent a @CYCLE@ expression for sequences. E.G.

> CYCLE

or

> NO CYCLE

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

@since 1.0.0.0
-}
newtype CycleExpr
  = CycleExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'CycleExpr' that indicates that the sequence should cycle.

  @since 1.0.0.0
-}
cycle :: CycleExpr
cycle = CycleExpr $ RawSql.fromString "CYCLE"

{- |
  Constructs a 'CycleExpr' that indicates that the sequence should not cycle.

  @since 1.0.0.0
-}
noCycle :: CycleExpr
noCycle = CycleExpr $ RawSql.fromString "NO CYCLE"

{- |
  Constructs a 'CycleExpr' that will cause the sequence to cycle if the flag
  passed is @True@.

  @since 1.0.0.0
-}
cycleIfTrue :: Bool -> CycleExpr
cycleIfTrue cycleFlag =
  if cycleFlag
    then cycle
    else noCycle

{- |
Type to represent a @DROP SEQUENCE@ statement. E.G.

> DROP SEQUENCE foo

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

@since 1.0.0.0
-}
newtype DropSequenceExpr
  = DropSequenceExpr RawSql.RawSql
  deriving (RawSql.SqlExpression)

{- |
  Constructs a 'DropSequenceExpr' that will drop sequence with the given name.
  You may specify an 'IfExists' argument if you want to include an @IF EXISTS@
  condition in the statement.

  @since 1.0.0.0
-}
dropSequenceExpr :: Maybe IfExists -> Qualified SequenceName -> DropSequenceExpr
dropSequenceExpr maybeIfExists sequenceName =
  DropSequenceExpr $
    RawSql.intercalate
      RawSql.space
      ( catMaybes
          [ Just (RawSql.fromString "DROP SEQUENCE")
          , fmap RawSql.toRawSql maybeIfExists
          , Just (RawSql.toRawSql sequenceName)
          ]
      )

{- |
  Constructs a 'ValueExpression' that will use the @nextval@ PostgreSQL
  function to get the next value from the given sequence. If you're trying to
  construct your own @SELECT@ to get the value of the sequence, you can use the
  constructed 'ValueExpression' with 'Orville.PostgreSQL.Expr.deriveColumnAs'
  to build the item to select.

  @since 1.0.0.0
-}
nextVal :: Qualified SequenceName -> ValueExpression
nextVal sequenceName =
  functionCall
    nextValFunction
    [valueExpression . SqlValue.fromRawBytes . RawSql.toExampleBytes $ sequenceName]

{- |
  The @nextval@ PostgreSQL function.

  @since 1.0.0.0
-}
nextValFunction :: FunctionName
nextValFunction =
  functionName "nextval"

{- |
  Constructs a 'ValueExpression' that will use the @currval@ PostgreSQL
  function to get the current value from the given sequence. If you're trying to
  construct your own @SELECT@ to get the value of the sequence, you can use the
  constructed 'ValueExpression' with 'Orville.PostgreSQL.Expr.deriveColumnAs'
  to build the item to select.

  @since 1.0.0.0
-}
currVal :: Qualified SequenceName -> ValueExpression
currVal sequenceName =
  functionCall
    currValFunction
    [valueExpression . SqlValue.fromRawBytes . RawSql.toExampleBytes $ sequenceName]

{- |
  The @currval@ PostgreSQL function.

  @since 1.0.0.0
-}
currValFunction :: FunctionName
currValFunction =
  functionName "currval"

{- |
  Constructs a 'ValueExpression' that will use the @setval@ PostgreSQL function
  to set the value from the given sequence. If you're trying to construct your
  own @SELECT@ to set the value of the sequence, you can use the constructed
  'ValueExpression' with 'Orville.PostgreSQL.Expr.deriveColumnAs' to build the
  item to select.

  @since 1.0.0.0
-}
setVal :: Qualified SequenceName -> Int64 -> ValueExpression
setVal sequenceName newValue =
  functionCall
    setValFunction
    [ valueExpression . SqlValue.fromRawBytes . RawSql.toExampleBytes $ sequenceName
    , valueExpression . SqlValue.fromInt64 $ newValue
    ]

{- |
  The @setval@ PostgreSQL function.

  @since 1.0.0.0
-}
setValFunction :: FunctionName
setValFunction =
  functionName "setval"