2019-12-09 01:17:39 +03:00
|
|
|
{-# LANGUAGE Arrows #-}
|
|
|
|
|
|
|
|
module Hasura.RQL.DDL.Schema.Cache.Fields
|
|
|
|
( addNonColumnFields
|
|
|
|
, mkRelationshipMetadataObject
|
|
|
|
, mkComputedFieldMetadataObject
|
|
|
|
) where
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
|
|
|
import qualified Data.HashMap.Strict.Extended as M
|
2019-12-09 07:18:53 +03:00
|
|
|
import qualified Data.HashSet as HS
|
2019-12-09 01:17:39 +03:00
|
|
|
import qualified Data.Sequence as Seq
|
|
|
|
|
|
|
|
import Control.Arrow.Extended
|
|
|
|
import Data.Aeson
|
|
|
|
|
|
|
|
import qualified Hasura.Incremental as Inc
|
|
|
|
|
|
|
|
import Hasura.RQL.DDL.ComputedField
|
|
|
|
import Hasura.RQL.DDL.Relationship
|
|
|
|
import Hasura.RQL.DDL.Schema.Cache.Common
|
|
|
|
import Hasura.RQL.DDL.Schema.Function
|
|
|
|
import Hasura.RQL.Types
|
|
|
|
import Hasura.RQL.Types.Catalog
|
|
|
|
import Hasura.SQL.Types
|
|
|
|
|
|
|
|
-- see Note [Specialization of buildRebuildableSchemaCache] in Hasura.RQL.DDL.Schema.Cache
|
|
|
|
{-# SPECIALIZE addNonColumnFields
|
|
|
|
:: CacheBuildA
|
2019-12-09 07:18:53 +03:00
|
|
|
( HashMap QualifiedTable TableRawInfo
|
2019-12-09 01:17:39 +03:00
|
|
|
, FieldInfoMap PGColumnInfo
|
|
|
|
, [CatalogRelation]
|
|
|
|
, [CatalogComputedField]
|
|
|
|
) (FieldInfoMap FieldInfo) #-}
|
|
|
|
|
|
|
|
addNonColumnFields
|
|
|
|
:: ( Inc.ArrowDistribute arr, ArrowWriter (Seq CollectedInfo) arr
|
|
|
|
, ArrowKleisli m arr, MonadError QErr m )
|
2019-12-09 07:18:53 +03:00
|
|
|
=> ( HashMap QualifiedTable TableRawInfo
|
2019-12-09 01:17:39 +03:00
|
|
|
, FieldInfoMap PGColumnInfo
|
|
|
|
, [CatalogRelation]
|
|
|
|
, [CatalogComputedField]
|
|
|
|
) `arr` FieldInfoMap FieldInfo
|
|
|
|
addNonColumnFields =
|
2019-12-09 07:18:53 +03:00
|
|
|
proc (rawTableInfo, columns, relationships, computedFields) -> do
|
|
|
|
let foreignKeys = _tciForeignKeys <$> rawTableInfo
|
2019-12-09 01:17:39 +03:00
|
|
|
relationshipInfos <-
|
|
|
|
(| Inc.keyed (\_ relationshipsByName -> do
|
|
|
|
maybeRelationship <- noDuplicates mkRelationshipMetadataObject -< relationshipsByName
|
|
|
|
(\info -> join info >- returnA) <-<
|
|
|
|
(| traverseA (\relationship -> do
|
|
|
|
info <- buildRelationship -< (foreignKeys, relationship)
|
|
|
|
returnA -< info <&> (, mkRelationshipMetadataObject relationship))
|
|
|
|
|) maybeRelationship)
|
|
|
|
|) (M.groupOn _crRelName relationships)
|
|
|
|
|
2019-12-09 07:18:53 +03:00
|
|
|
let trackedTableNames = HS.fromList $ M.keys rawTableInfo
|
2019-12-09 01:17:39 +03:00
|
|
|
computedFieldInfos <-
|
|
|
|
(| Inc.keyed (\_ computedFieldsByName -> do
|
|
|
|
maybeComputedField <- noDuplicates mkComputedFieldMetadataObject -< computedFieldsByName
|
|
|
|
(\info -> join info >- returnA) <-<
|
|
|
|
(| traverseA (\computedField -> do
|
|
|
|
info <- buildComputedField -< (trackedTableNames, computedField)
|
|
|
|
returnA -< info <&> (, mkComputedFieldMetadataObject computedField))
|
|
|
|
|) maybeComputedField)
|
|
|
|
|) (M.groupOn (_afcName . _cccComputedField) computedFields)
|
|
|
|
|
|
|
|
let mapKey f = M.fromList . map (first f) . M.toList
|
|
|
|
relationshipFields = mapKey fromRel $ M.catMaybes relationshipInfos
|
|
|
|
computedFieldFields = mapKey fromComputedField $ M.catMaybes computedFieldInfos
|
|
|
|
nonColumnFields <-
|
|
|
|
(| Inc.keyed (\fieldName fields -> noFieldConflicts -< (fieldName, fields))
|
|
|
|
|) (align relationshipFields computedFieldFields)
|
|
|
|
|
|
|
|
(| Inc.keyed (\_ fields -> noColumnConflicts -< fields)
|
|
|
|
|) (align columns (M.catMaybes nonColumnFields))
|
|
|
|
where
|
|
|
|
noFieldConflicts = proc (fieldName, fields) -> case fields of
|
|
|
|
This (relationship, metadata) -> returnA -< Just (FIRelationship relationship, metadata)
|
|
|
|
That (computedField, metadata) -> returnA -< Just (FIComputedField computedField, metadata)
|
|
|
|
These (_, relationshipMetadata) (_, computedFieldMetadata) -> do
|
|
|
|
tellA -< Seq.singleton $ CIInconsistency $ ConflictingObjects
|
|
|
|
("conflicting definitions for field " <>> fieldName)
|
|
|
|
[relationshipMetadata, computedFieldMetadata]
|
|
|
|
returnA -< Nothing
|
|
|
|
|
|
|
|
noColumnConflicts = proc fields -> case fields of
|
|
|
|
This columnInfo -> returnA -< FIColumn columnInfo
|
|
|
|
That (fieldInfo, _) -> returnA -< fieldInfo
|
|
|
|
These columnInfo (_, fieldMetadata) -> do
|
|
|
|
recordInconsistency -< (fieldMetadata, "field definition conflicts with postgres column")
|
|
|
|
returnA -< FIColumn columnInfo
|
|
|
|
|
|
|
|
mkRelationshipMetadataObject :: CatalogRelation -> MetadataObject
|
|
|
|
mkRelationshipMetadataObject (CatalogRelation qt rn rt rDef cmnt) =
|
|
|
|
let objectId = MOTableObj qt $ MTORel rn rt
|
|
|
|
definition = toJSON $ WithTable qt $ RelDef rn rDef cmnt
|
|
|
|
in MetadataObject objectId definition
|
|
|
|
|
|
|
|
buildRelationship
|
2019-12-09 07:18:53 +03:00
|
|
|
:: (Inc.ArrowDistribute arr, ArrowWriter (Seq CollectedInfo) arr)
|
|
|
|
=> (HashMap QualifiedTable [ForeignKey], CatalogRelation) `arr` Maybe RelInfo
|
2019-12-09 01:17:39 +03:00
|
|
|
buildRelationship = proc (foreignKeys, relationship) -> do
|
|
|
|
let CatalogRelation tableName rn rt rDef _ = relationship
|
|
|
|
metadataObject = mkRelationshipMetadataObject relationship
|
|
|
|
schemaObject = SOTableObj tableName $ TORel rn
|
|
|
|
addRelationshipContext e = "in relationship " <> rn <<> ": " <> e
|
|
|
|
(| withRecordInconsistency (
|
|
|
|
(| modifyErrA (do
|
2019-12-09 07:18:53 +03:00
|
|
|
(info, dependencies) <- liftEitherA -< case rt of
|
2019-12-09 01:17:39 +03:00
|
|
|
ObjRel -> do
|
|
|
|
using <- decodeValue rDef
|
2019-12-09 07:18:53 +03:00
|
|
|
tableForeignKeys <- getTableInfo tableName foreignKeys
|
|
|
|
objRelP2Setup tableName tableForeignKeys (RelDef rn using Nothing)
|
2019-12-09 01:17:39 +03:00
|
|
|
ArrRel -> do
|
|
|
|
using <- decodeValue rDef
|
2019-12-09 07:18:53 +03:00
|
|
|
arrRelP2Setup foreignKeys tableName (RelDef rn using Nothing)
|
2019-12-09 01:17:39 +03:00
|
|
|
recordDependencies -< (metadataObject, schemaObject, dependencies)
|
|
|
|
returnA -< info)
|
|
|
|
|) (addTableContext tableName . addRelationshipContext))
|
|
|
|
|) metadataObject
|
|
|
|
|
|
|
|
mkComputedFieldMetadataObject :: CatalogComputedField -> MetadataObject
|
|
|
|
mkComputedFieldMetadataObject (CatalogComputedField column _) =
|
|
|
|
let AddComputedField qt name _ _ = column
|
|
|
|
objectId = MOTableObj qt $ MTOComputedField name
|
|
|
|
in MetadataObject objectId (toJSON column)
|
|
|
|
|
|
|
|
buildComputedField
|
|
|
|
:: ( Inc.ArrowDistribute arr, ArrowWriter (Seq CollectedInfo) arr
|
|
|
|
, ArrowKleisli m arr, MonadError QErr m )
|
|
|
|
=> (HashSet QualifiedTable, CatalogComputedField) `arr` Maybe ComputedFieldInfo
|
|
|
|
buildComputedField = proc (trackedTableNames, computedField) -> do
|
|
|
|
let CatalogComputedField column funcDefs = computedField
|
|
|
|
AddComputedField qt name def comment = column
|
|
|
|
addComputedFieldContext e = "in computed field " <> name <<> ": " <> e
|
|
|
|
(| withRecordInconsistency (
|
|
|
|
(| modifyErrA (do
|
|
|
|
rawfi <- bindErrorA -< handleMultipleFunctions (_cfdFunction def) funcDefs
|
|
|
|
bindErrorA -< addComputedFieldP2Setup trackedTableNames qt name def rawfi comment)
|
|
|
|
|) (addTableContext qt . addComputedFieldContext))
|
|
|
|
|) (mkComputedFieldMetadataObject computedField)
|