packages feed

orville-postgresql-1.0.0.0: src/Orville/PostgreSQL/PgCatalog/PgClass.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

@since 1.0.0.0
-}
module Orville.PostgreSQL.PgCatalog.PgClass
  ( PgClass (..)
  , RelationName
  , relationNameToString
  , RelationKind (..)
  , pgClassTable
  , relationNameField
  , namespaceOidField
  , relationKindField
  )
where

import qualified Data.String as String
import qualified Data.Text as T
import qualified Database.PostgreSQL.LibPQ as LibPQ

import qualified Orville.PostgreSQL as Orville
import Orville.PostgreSQL.PgCatalog.OidField (oidField, oidTypeField)

{- |
  The Haskell representation of data read from the @pg_catalog.pg_class@
  table. Rows in this table correspond to tables, indexes, sequences, views,
  materialized views, composite types and TOAST tables.

@since 1.0.0.0
-}
data PgClass = PgClass
  { pgClassOid :: LibPQ.Oid
  -- ^ The PostgreSQL @oid@ for the relation.
  , pgClassNamespaceOid :: LibPQ.Oid
  -- ^ The PostgreSQL @oid@ of the namespace that the relation belongs to.
  -- References @pg_namespace.oid@.
  , pgClassRelationName :: RelationName
  -- ^ The name of the relation.
  , pgClassRelationKind :: RelationKind
  -- ^ The kind of relation (table, view, etc).
  }

{- |
  A Haskell type for the name of the relation represented by a 'PgClass'.

@since 1.0.0.0
-}
newtype RelationName
  = RelationName T.Text
  deriving (Show, Eq, Ord, String.IsString)

{- |
  Convert a 'RelationName' to a plain 'String'.

@since 1.0.0.0
-}
relationNameToString :: RelationName -> String
relationNameToString (RelationName text) =
  T.unpack text

{- |
  The kind of relation represented by a 'PgClass', as described at
  https://www.postgresql.org/docs/13/catalog-pg-class.html.

@since 1.0.0.0
-}
data RelationKind
  = OrdinaryTable
  | Index
  | Sequence
  | ToastTable
  | View
  | MaterializedView
  | CompositeType
  | ForeignTable
  | PartitionedTable
  | PartitionedIndex
  deriving (Show, Eq)

{- |
  An Orville 'Orville.TableDefinition' for querying the
  @pg_catalog.pg_class@ table.

@since 1.0.0.0
-}
pgClassTable :: Orville.TableDefinition (Orville.HasKey LibPQ.Oid) PgClass PgClass
pgClassTable =
  Orville.setTableSchema "pg_catalog" $
    Orville.mkTableDefinition
      "pg_class"
      (Orville.primaryKey oidField)
      pgClassMarshaller

pgClassMarshaller :: Orville.SqlMarshaller PgClass PgClass
pgClassMarshaller =
  PgClass
    <$> Orville.marshallField pgClassOid oidField
    <*> Orville.marshallField pgClassNamespaceOid namespaceOidField
    <*> Orville.marshallField pgClassRelationName relationNameField
    <*> Orville.marshallField pgClassRelationKind relationKindField

{- |
  The @relnamespace@ column of the @pg_catalog.pg_class@ table.

@since 1.0.0.0
-}
namespaceOidField :: Orville.FieldDefinition Orville.NotNull LibPQ.Oid
namespaceOidField =
  oidTypeField "relnamespace"

{- |
  The @relname@ column of the @pg_catalog.pg_class@ table.

@since 1.0.0.0
-}
relationNameField :: Orville.FieldDefinition Orville.NotNull RelationName
relationNameField =
  Orville.coerceField $
    Orville.unboundedTextField "relname"

{- |
  The @relkind@ column of the @pg_catalog.pg_class@ table.

@since 1.0.0.0
-}
relationKindField :: Orville.FieldDefinition Orville.NotNull RelationKind
relationKindField =
  Orville.convertField
    (Orville.tryConvertSqlType relationKindToPgText pgTextToRelationKind)
    (Orville.unboundedTextField "relkind")

{- |
  Converts a 'RelationKind' to the corresponding single character text
  representation used by PostgreSQL.

  See also 'pgTextToRelationKind'

@since 1.0.0.0
-}
relationKindToPgText :: RelationKind -> T.Text
relationKindToPgText kind =
  T.pack $
    case kind of
      OrdinaryTable -> "r"
      Index -> "i"
      Sequence -> "S"
      ToastTable -> "t"
      View -> "v"
      MaterializedView -> "m"
      CompositeType -> "c"
      ForeignTable -> "f"
      PartitionedTable -> "p"
      PartitionedIndex -> "I"

{- |
  Attempts to parse a PostgreSQL single character textual value as a
  'RelationKind'.

  See also 'relationKindToPgText'

@since 1.0.0.0
-}
pgTextToRelationKind :: T.Text -> Either String RelationKind
pgTextToRelationKind text =
  case T.unpack text of
    "r" -> Right OrdinaryTable
    "i" -> Right Index
    "S" -> Right Sequence
    "t" -> Right ToastTable
    "v" -> Right View
    "m" -> Right MaterializedView
    "c" -> Right CompositeType
    "f" -> Right ForeignTable
    "p" -> Right PartitionedTable
    "I" -> Right PartitionedIndex
    kind -> Left ("Unrecognized PostgreSQL relation kind: " <> kind)