{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.Internal.Name.Qualified
  ( Qualified
  , qualifyTable
  , qualifySequence
  , qualifyColumn
  )
where

import Orville.PostgreSQL.Expr.Internal.Name.ColumnName (ColumnName)
import Orville.PostgreSQL.Expr.Internal.Name.Identifier (IdentifierExpression (toIdentifier))
import Orville.PostgreSQL.Expr.Internal.Name.SchemaName (SchemaName)
import Orville.PostgreSQL.Expr.Internal.Name.SequenceName (SequenceName)
import Orville.PostgreSQL.Expr.Internal.Name.TableName (TableName)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql

{- |
Type to represent a qualified SQL name. E.G.

> "some_schema_name"."some_table_name"

'Qualified' 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 Qualified name
  = Qualified RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql -> Qualified name
Qualified name -> RawSql
(Qualified name -> RawSql)
-> (RawSql -> Qualified name) -> SqlExpression (Qualified name)
forall name. RawSql -> Qualified name
forall name. Qualified name -> RawSql
forall a. (a -> RawSql) -> (RawSql -> a) -> SqlExpression a
$ctoRawSql :: forall name. Qualified name -> RawSql
toRawSql :: Qualified name -> RawSql
$cunsafeFromRawSql :: forall name. RawSql -> Qualified name
unsafeFromRawSql :: RawSql -> Qualified name
RawSql.SqlExpression
    )

{- |
Optionally qualifies a 'TableName' with a 'SchemaName'.

Note: If you already have a 'Orville.PostgreSQL.Schema.TableIdentifier' in
hand you should probably use
'Orville.PostgreSQL.Schema.tableIdQualifiedName' instead.
@since 1.0.0.0
-}
qualifyTable ::
  Maybe SchemaName ->
  TableName ->
  Qualified TableName
qualifyTable :: Maybe SchemaName -> TableName -> Qualified TableName
qualifyTable = Maybe SchemaName -> TableName -> Qualified TableName
forall name.
IdentifierExpression name =>
Maybe SchemaName -> name -> Qualified name
unsafeSchemaQualify

{- |
Optionally qualifies a 'SequenceName' with a 'SchemaName'.

Note: If you already have a 'Orville.PostgreSQL.Schema.SequenceIdentifier' in
hand you should probably use
'Orville.PostgreSQL.Schema.sequenceIdQualifiedName' instead.

@since 1.0.0.0
-}
qualifySequence ::
  Maybe SchemaName ->
  SequenceName ->
  Qualified SequenceName
qualifySequence :: Maybe SchemaName -> SequenceName -> Qualified SequenceName
qualifySequence = Maybe SchemaName -> SequenceName -> Qualified SequenceName
forall name.
IdentifierExpression name =>
Maybe SchemaName -> name -> Qualified name
unsafeSchemaQualify

{- |
Qualifies a 'ColumnName' with a 'TableName' and, optionally, a 'SchemaName'.
This should be used to refer to the column in SQL queries where a qualified
reference is appropriate.

@since 1.0.0.0
-}
qualifyColumn :: Maybe SchemaName -> TableName -> ColumnName -> Qualified ColumnName
qualifyColumn :: Maybe SchemaName -> TableName -> ColumnName -> Qualified ColumnName
qualifyColumn Maybe SchemaName
mbSchemaName TableName
tableName ColumnName
unqualifiedName =
  Maybe SchemaName -> ColumnName -> Qualified ColumnName
forall name.
IdentifierExpression name =>
Maybe SchemaName -> name -> Qualified name
unsafeSchemaQualify Maybe SchemaName
mbSchemaName
    (ColumnName -> Qualified ColumnName)
-> (RawSql -> ColumnName) -> RawSql -> Qualified ColumnName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RawSql -> ColumnName
forall a. SqlExpression a => RawSql -> a
RawSql.unsafeFromRawSql
    (RawSql -> Qualified ColumnName) -> RawSql -> Qualified ColumnName
forall a b. (a -> b) -> a -> b
$ Identifier -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql (TableName -> Identifier
forall name. IdentifierExpression name => name -> Identifier
toIdentifier TableName
tableName) RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.dot RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> Identifier -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql (ColumnName -> Identifier
forall name. IdentifierExpression name => name -> Identifier
toIdentifier ColumnName
unqualifiedName)

-- Note: Not everything actually makes sense to be qualified by _only_ a schema name, such as
-- columns, as in 'qualifyColumn'. But this does give us a nice uniform way to provide the
-- functionality in those more type restricted scenarios.
unsafeSchemaQualify ::
  IdentifierExpression name =>
  Maybe SchemaName ->
  name ->
  Qualified name
unsafeSchemaQualify :: forall name.
IdentifierExpression name =>
Maybe SchemaName -> name -> Qualified name
unsafeSchemaQualify Maybe SchemaName
mbSchemaName name
unqualifiedName =
  case Maybe SchemaName
mbSchemaName of
    Maybe SchemaName
Nothing ->
      RawSql -> Qualified name
forall name. RawSql -> Qualified name
Qualified (RawSql -> Qualified name)
-> (name -> RawSql) -> name -> Qualified name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Identifier -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql (Identifier -> RawSql) -> (name -> Identifier) -> name -> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. name -> Identifier
forall name. IdentifierExpression name => name -> Identifier
toIdentifier (name -> Qualified name) -> name -> Qualified name
forall a b. (a -> b) -> a -> b
$ name
unqualifiedName
    Just SchemaName
schemaName ->
      RawSql -> Qualified name
forall name. RawSql -> Qualified name
Qualified
        ( SchemaName -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql SchemaName
schemaName
            RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.dot
            RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> Identifier -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql (name -> Identifier
forall name. IdentifierExpression name => name -> Identifier
toIdentifier name
unqualifiedName)
        )