packages feed

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

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.Cursor
  ( DeclareExpr
  , declare
  , ScrollExpr
  , scroll
  , noScroll
  , HoldExpr
  , withHold
  , withoutHold
  , CloseExpr
  , close
  , AllCursors
  , allCursors
  , FetchExpr
  , fetch
  , MoveExpr
  , move
  , CursorDirection
  , next
  , prior
  , first
  , last
  , absolute
  , relative
  , rowCount
  , fetchAll
  , forward
  , forwardCount
  , forwardAll
  , backward
  , backwardCount
  , backwardAll
  )
where

import Data.Maybe (catMaybes)
import Prelude (Either, Int, Maybe (Just), either, fmap, ($), (.), (<>))

import Orville.PostgreSQL.Expr.Name (CursorName)
import Orville.PostgreSQL.Expr.Query (QueryExpr)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql

{- |
'DeclareExpr' corresponds to the SQL DECLARE statement, for declaring and
opening cursors. E.G.

> DECLARE FOO CURSOR FOR SELECT * FROM BAR

See PostgreSQL [cursor declare
documentation](https://www.postgresql.org/docs/current/sql-declare.html) for
more information.

'DeclareExpr' 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 DeclareExpr
  = DeclareExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | A smart constructor for setting up a 'DeclareExpr'. This, along with other functions provided,
   allows users to more safely declare a cursor.

@since 1.0.0.0
-}
declare ::
  CursorName ->
  Maybe ScrollExpr ->
  Maybe HoldExpr ->
  QueryExpr ->
  DeclareExpr
declare cursorName maybeScrollExpr maybeHoldExpr queryExpr =
  DeclareExpr $
    RawSql.intercalate RawSql.space $
      catMaybes
        [ Just $ RawSql.fromString "DECLARE"
        , Just $ RawSql.toRawSql cursorName
        , fmap RawSql.toRawSql maybeScrollExpr
        , Just $ RawSql.fromString "CURSOR"
        , fmap RawSql.toRawSql maybeHoldExpr
        , Just $ RawSql.fromString "FOR"
        , Just $ RawSql.toRawSql queryExpr
        ]

{- |
'ScrollExpr' is used to determine if a cursor should be able to fetch
nonsequentially. E.G.

> NO SCROLL

Note that the default in at least PostgreSQL versions 11-15 is to allow
nonsequential fetches under some, but not all, circumstances.

See PostgreSQL [cursor declare
documentation](https://www.postgresql.org/docs/current/sql-declare.html) for more information.

'ScrollExpr' 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 ScrollExpr
  = ScrollExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Allow a cursor to be used to fetch rows nonsequentially.

@since 1.0.0.0
-}
scroll :: ScrollExpr
scroll =
  ScrollExpr . RawSql.fromString $ "SCROLL"

{- | Only allow a cursor to be used to fetch rows sequentially.

@since 1.0.0.0
-}
noScroll :: ScrollExpr
noScroll =
  ScrollExpr . RawSql.fromString $ "NO SCROLL"

{- |
'HoldExpr' is used to determine if a cursor should be available for use after
the transaction that created it has been committed. E.G.

> WITH HOLD

See PostgreSQL [cursor documentation](https://www.postgresql.org/docs/current/sql-declare.html) for
more information.

'HoldExpr' 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 HoldExpr
  = HoldExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Allow a cursor to be used after the transaction creating it is committed.

@since 1.0.0.0
-}
withHold :: HoldExpr
withHold =
  HoldExpr . RawSql.fromString $ "WITH HOLD"

{- | Do not allow a cursor to be used after the transaction creating it is committed.

@since 1.0.0.0
-}
withoutHold :: HoldExpr
withoutHold =
  HoldExpr . RawSql.fromString $ "WITHOUT HOLD"

{- |
'CloseExpr' corresponds to the SQL CLOSE statement. E.G.

> CLOSE ALL

See PostgreSQL [close documentation](https://www.postgresql.org/docs/current/sql-close.html) for
more information.

'HoldExpr' 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 CloseExpr
  = CloseExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | A smart constructor for setting up a 'CloseExpr', either closing all cursors or the given named
   cursor.

@since 1.0.0.0
-}
close :: Either AllCursors CursorName -> CloseExpr
close allOrCursorName =
  CloseExpr $
    RawSql.fromString "CLOSE "
      <> either RawSql.toRawSql RawSql.toRawSql allOrCursorName

{- |
'AllCursors' corresponds to the ALL keyword in a CLOSE statement. E.G.

> ALL

'AllCursors' 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 AllCursors
  = AllCursors RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Specify closing all open cursors, for use with a 'CloseExpr'.

@since 1.0.0.0
-}
allCursors :: AllCursors
allCursors =
  AllCursors . RawSql.fromString $ "ALL"

{- |
'FetchExpr' corresponds to the SQL FETCH statement, for retrieving rows from a
previously-created cursor. E.G.

> FETCH NEXT FOO

See PostgreSQL [fetch
documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for more
information.

'FetchExpr' 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 FetchExpr
  = FetchExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Construct a 'FetchExpr', for a given cursor and optionally a direction to fetch.

@since 1.0.0.0
-}
fetch :: Maybe CursorDirection -> CursorName -> FetchExpr
fetch maybeDirection cursorName =
  FetchExpr $
    RawSql.intercalate RawSql.space $
      catMaybes
        [ Just $ RawSql.fromString "FETCH"
        , fmap RawSql.toRawSql maybeDirection
        , Just $ RawSql.toRawSql cursorName
        ]

{- |
'MoveExpr' corresponds to the SQL MOVE statement, for positioning a previously
created cursor, /without/ retrieving any rows. E.G.

> MOVE NEXT FOO

'MoveExpr' 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 MoveExpr
  = MoveExpr RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Construct a 'MoveExpr', for a given cursor and optionally a direction to move.

@since 1.0.0.0
-}
move :: Maybe CursorDirection -> CursorName -> MoveExpr
move maybeDirection cursorName =
  MoveExpr
    . RawSql.intercalate RawSql.space
    $ catMaybes
      [ Just $ RawSql.fromString "MOVE"
      , fmap RawSql.toRawSql maybeDirection
      , Just $ RawSql.toRawSql cursorName
      ]

{- |
'CursorDirection' corresponds to the direction argument to the SQL FETCH and
MOVE statements. E.G.

> BACKWARD

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

'CursorDirection' 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 CursorDirection
  = CursorDirection RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql.SqlExpression
    )

{- | Specify the direction of the next single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
next :: CursorDirection
next =
  CursorDirection . RawSql.fromString $ "NEXT"

{- | Specify the direction of the prior single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
prior :: CursorDirection
prior =
  CursorDirection . RawSql.fromString $ "PRIOR"

{- | Specify the direction of the first single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
first :: CursorDirection
first =
  CursorDirection . RawSql.fromString $ "FIRST"

{- | Specify the direction of the last single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
last :: CursorDirection
last =
  CursorDirection . RawSql.fromString $ "LAST"

{- | Specify the direction of the single row at an absolute position within the
    cursor. Primarily for use with 'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
absolute :: Int -> CursorDirection
absolute countParam =
  -- postgresql won't let us pass the count as a parameter.
  -- when we try we get an error like such error:
  --  ERROR:  syntax error at or near "$1"
  --  LINE 1: FETCH ABSOLUTE $1 \"testcursor\"
  CursorDirection $
    RawSql.fromString "ABSOLUTE "
      <> RawSql.intDecLiteral countParam

{- | Specify the direction of the single row relative to the cursor's current
    position. Primarily for use with 'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
relative :: Int -> CursorDirection
relative countParam =
  CursorDirection $
    RawSql.fromString "RELATIVE "
      <>
      -- postgresql won't let us pass the count as a parameter.
      -- when we try we get an error like such error:
      --  ERROR:  syntax error at or near "$1"
      --  LINE 1: FETCH RELATIVE $1 \"testcursor\"
      RawSql.intDecLiteral countParam

{- | Specify the direction of the next n rows. Primarily for use with 'fetch'
    or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
rowCount :: Int -> CursorDirection
rowCount countParam =
  -- postgresql won't let us pass the count as a parameter.
  -- when we try we get an error like such error:
  --  ERROR:  syntax error at or near "$1"
  --  LINE 1: FETCH $1 \"testcursor\"
  CursorDirection $
    RawSql.intDecLiteral countParam

{- | Specify the direction of all the next rows. Primarily for use with 'fetch'
    or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
fetchAll :: CursorDirection
fetchAll =
  CursorDirection . RawSql.fromString $ "ALL"

{- | Specify the direction of the next single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
forward :: CursorDirection
forward =
  CursorDirection . RawSql.fromString $ "FORWARD"

{- | Specify the direction of the next n rows. Primarily for use with 'fetch'
    or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
forwardCount :: Int -> CursorDirection
forwardCount countParam =
  -- postgresql won't let us pass the count as a parameter.
  -- when we try we get an error like such error:
  --  ERROR:  syntax error at or near "$1"
  --  LINE 1: FETCH FORWARD $1 \"testcursor\"
  CursorDirection $
    RawSql.fromString "FORWARD "
      <> RawSql.intDecLiteral countParam

{- | Specify the direction of all the next rows. Primarily for use with 'fetch'
    or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
forwardAll :: CursorDirection
forwardAll =
  CursorDirection . RawSql.fromString $ "FORWARD ALL"

{- | Specify the direction of the prior single row. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
backward :: CursorDirection
backward =
  CursorDirection . RawSql.fromString $ "BACKWARD"

{- | Specify the direction of the prior n rows. Primarily for use with 'fetch'
    or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
backwardCount :: Int -> CursorDirection
backwardCount countParam =
  -- postgresql won't let us pass the count as a parameter.
  -- when we try we get an error like such error:
  --  ERROR:  syntax error at or near "$1"
  --  LINE 1: FETCH BACKWARD $1 \"testcursor\"
  CursorDirection $
    RawSql.fromString "BACKWARD "
      <> RawSql.intDecLiteral countParam

{- | Specify the direction of all the prior rows. Primarily for use with
    'fetch' or 'move'.

See PostgreSQL [fetch documentation](https://www.postgresql.org/docs/current/sql-fetch.html) for
more information.

@since 1.0.0.0
-}
backwardAll :: CursorDirection
backwardAll =
  CursorDirection . RawSql.fromString $ "BACKWARD ALL"