graphql-engine/server/src-lib/Hasura/Backends/Postgres/Instances/Metadata.hs

142 lines
5.7 KiB
Haskell
Raw Normal View History

{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Postgres Instances Metadata
--
-- Defines a 'Hasura.RQL.Types.Metadata.Backend.BackendMetadata' type class instance for Postgres.
module Hasura.Backends.Postgres.Instances.Metadata () where
import Data.HashMap.Strict qualified as Map
import Data.Text.Extended
import Hasura.Backends.Postgres.DDL qualified as Postgres
import Hasura.Backends.Postgres.Instances.LogicalModels as Postgres (validateLogicalModel)
import Hasura.Backends.Postgres.SQL.Types (QualifiedTable)
import Hasura.Backends.Postgres.Types.CitusExtraTableMetadata
import Hasura.Base.Error
import Hasura.Prelude
import Hasura.RQL.Types.Backend (Backend)
import Hasura.RQL.Types.Metadata.Backend
import Hasura.RQL.Types.Relationships.Local
import Hasura.RQL.Types.Table
import Hasura.SQL.Backend
--------------------------------------------------------------------------------
-- PostgresMetadata
-- | We differentiate the handling of metadata between Citus and Vanilla
-- Postgres because Citus imposes limitations on the types of joins that it
-- permits, which then limits the types of relations that we can track.
class PostgresMetadata (pgKind :: PostgresKind) where
-- TODO: find a better name
validateRel ::
MonadError QErr m =>
TableCache ('Postgres pgKind) ->
QualifiedTable ->
Either (ObjRelDef ('Postgres pgKind)) (ArrRelDef ('Postgres pgKind)) ->
m ()
instance PostgresMetadata 'Vanilla where
validateRel _ _ _ = pure ()
instance PostgresMetadata 'Citus where
validateRel ::
forall m.
MonadError QErr m =>
TableCache ('Postgres 'Citus) ->
QualifiedTable ->
Either (ObjRelDef ('Postgres 'Citus)) (ArrRelDef ('Postgres 'Citus)) ->
m ()
validateRel tableCache sourceTable relInfo = do
sourceTableInfo <- lookupTableInfo sourceTable
case relInfo of
Left (RelDef _ obj _) ->
case obj of
RUFKeyOn (SameTable _) -> pure ()
RUFKeyOn (RemoteTable targetTable _) -> checkObjectRelationship sourceTableInfo targetTable
RUManual RelManualConfig {} -> pure ()
Right (RelDef _ obj _) ->
case obj of
RUFKeyOn (ArrRelUsingFKeyOn targetTable _col) -> checkArrayRelationship sourceTableInfo targetTable
RUManual RelManualConfig {} -> pure ()
where
lookupTableInfo tableName =
Map.lookup tableName tableCache
`onNothing` throw400 NotFound ("no such table " <>> tableName)
checkObjectRelationship sourceTableInfo targetTable = do
targetTableInfo <- lookupTableInfo targetTable
let notSupported = throwNotSupportedError sourceTableInfo targetTableInfo "object"
case ( _tciExtraTableMetadata $ _tiCoreInfo sourceTableInfo,
_tciExtraTableMetadata $ _tiCoreInfo targetTableInfo
) of
(Distributed {}, Local) -> notSupported
(Distributed {}, Reference) -> pure ()
(Distributed {}, Distributed {}) -> pure ()
(_, Distributed {}) -> notSupported
(_, _) -> pure ()
checkArrayRelationship sourceTableInfo targetTable = do
targetTableInfo <- lookupTableInfo targetTable
let notSupported = throwNotSupportedError sourceTableInfo targetTableInfo "array"
case ( _tciExtraTableMetadata $ _tiCoreInfo sourceTableInfo,
_tciExtraTableMetadata $ _tiCoreInfo targetTableInfo
) of
(Distributed {}, Distributed {}) -> pure ()
(Distributed {}, _) -> notSupported
(_, Distributed {}) -> notSupported
(_, _) -> pure ()
showDistributionType :: ExtraTableMetadata -> Text
showDistributionType = \case
Local -> "local"
Distributed _ -> "distributed"
Reference -> "reference"
throwNotSupportedError :: TableInfo ('Postgres 'Citus) -> TableInfo ('Postgres 'Citus) -> Text -> m ()
throwNotSupportedError sourceTableInfo targetTableInfo t =
let tciSrc = _tiCoreInfo sourceTableInfo
tciTgt = _tiCoreInfo targetTableInfo
in throw400
NotSupported
( showDistributionType (_tciExtraTableMetadata tciSrc)
<> " tables ("
<> toTxt (_tciName tciSrc)
<> ") cannot have an "
<> t
<> " relationship against a "
<> showDistributionType (_tciExtraTableMetadata $ _tiCoreInfo targetTableInfo)
<> " table ("
<> toTxt (_tciName tciTgt)
<> ")"
)
instance PostgresMetadata 'Cockroach where
validateRel _ _ _ = pure ()
----------------------------------------------------------------
-- BackendMetadata instance
instance
( Backend ('Postgres pgKind),
PostgresMetadata pgKind,
Postgres.FetchTableMetadata pgKind,
Postgres.FetchFunctionMetadata pgKind,
Postgres.ToMetadataFetchQuery pgKind
) =>
BackendMetadata ('Postgres pgKind)
where
prepareCatalog = Postgres.prepareCatalog
buildComputedFieldInfo = Postgres.buildComputedFieldInfo
fetchAndValidateEnumValues = Postgres.fetchAndValidateEnumValues
resolveSourceConfig = Postgres.resolveSourceConfig
resolveDatabaseMetadata = Postgres.resolveDatabaseMetadata
parseBoolExpOperations = Postgres.parseBoolExpOperations
buildFunctionInfo = Postgres.buildFunctionInfo
updateColumnInEventTrigger = Postgres.updateColumnInEventTrigger
parseCollectableType = Postgres.parseCollectableType
postDropSourceHook = Postgres.postDropSourceHook
validateRelationship = validateRel @pgKind
buildComputedFieldBooleanExp = Postgres.buildComputedFieldBooleanExp
validateLogicalModel = Postgres.validateLogicalModel
supportsBeingRemoteRelationshipTarget _ = True