2020-12-08 17:22:31 +03:00
|
|
|
|
-- | Funtions related to @hdb_catalog@ schema prior to metadata separation (catalog version < 43).
|
|
|
|
|
module Hasura.RQL.DDL.Schema.LegacyCatalog
|
|
|
|
|
( saveMetadataToHdbTables
|
|
|
|
|
, fetchMetadataFromHdbTables
|
|
|
|
|
, recreateSystemMetadata
|
|
|
|
|
) where
|
|
|
|
|
|
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
|
|
|
|
|
import qualified Data.HashMap.Strict as HM
|
|
|
|
|
import qualified Data.HashMap.Strict.InsOrd as OMap
|
|
|
|
|
import qualified Data.HashSet.InsOrd as HSIns
|
|
|
|
|
import qualified Data.Time.Clock as C
|
|
|
|
|
import qualified Database.PG.Query as Q
|
|
|
|
|
|
|
|
|
|
import Control.Lens hiding ((.=))
|
|
|
|
|
import Data.Aeson
|
2021-03-16 20:35:35 +03:00
|
|
|
|
import Data.FileEmbed (makeRelativeToProject)
|
2020-12-08 17:22:31 +03:00
|
|
|
|
import Data.Text.NonEmpty
|
|
|
|
|
|
|
|
|
|
import Hasura.Backends.Postgres.Connection
|
|
|
|
|
import Hasura.Backends.Postgres.SQL.Types
|
2021-05-11 18:18:31 +03:00
|
|
|
|
import Hasura.Base.Error
|
2020-12-08 17:22:31 +03:00
|
|
|
|
import Hasura.Eventing.ScheduledTrigger
|
|
|
|
|
import Hasura.RQL.DDL.ComputedField
|
|
|
|
|
import Hasura.RQL.DDL.Permission
|
|
|
|
|
import Hasura.RQL.Types
|
|
|
|
|
|
2021-05-11 18:18:31 +03:00
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
saveMetadataToHdbTables
|
|
|
|
|
:: (MonadTx m, HasSystemDefined m) => MetadataNoSources -> m ()
|
|
|
|
|
saveMetadataToHdbTables (MetadataNoSources tables functions schemas collections
|
2020-12-08 17:22:31 +03:00
|
|
|
|
allowlist customTypes actions cronTriggers) = do
|
|
|
|
|
|
|
|
|
|
withPathK "tables" $ do
|
|
|
|
|
indexedForM_ tables $ \TableMetadata{..} -> do
|
|
|
|
|
-- Save table
|
|
|
|
|
saveTableToCatalog _tmTable _tmIsEnum _tmConfiguration
|
|
|
|
|
|
|
|
|
|
-- Relationships
|
|
|
|
|
withPathK "object_relationships" $
|
|
|
|
|
indexedForM_ _tmObjectRelationships $ \objRel ->
|
|
|
|
|
insertRelationshipToCatalog _tmTable ObjRel objRel
|
|
|
|
|
withPathK "array_relationships" $
|
|
|
|
|
indexedForM_ _tmArrayRelationships $ \arrRel ->
|
|
|
|
|
insertRelationshipToCatalog _tmTable ArrRel arrRel
|
|
|
|
|
|
|
|
|
|
-- Computed Fields
|
|
|
|
|
withPathK "computed_fields" $
|
|
|
|
|
indexedForM_ _tmComputedFields $
|
|
|
|
|
\(ComputedFieldMetadata name definition comment) ->
|
|
|
|
|
addComputedFieldToCatalog $
|
2020-12-28 15:56:00 +03:00
|
|
|
|
AddComputedField defaultSource _tmTable name definition comment
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
-- Remote Relationships
|
|
|
|
|
withPathK "remote_relationships" $
|
|
|
|
|
indexedForM_ _tmRemoteRelationships $
|
|
|
|
|
\(RemoteRelationshipMetadata name def) -> do
|
|
|
|
|
let RemoteRelationshipDef rs hf rf = def
|
|
|
|
|
addRemoteRelationshipToCatalog $
|
2020-12-28 15:56:00 +03:00
|
|
|
|
RemoteRelationship name defaultSource _tmTable hf rs rf
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
-- Permissions
|
|
|
|
|
withPathK "insert_permissions" $ processPerms _tmTable _tmInsertPermissions
|
|
|
|
|
withPathK "select_permissions" $ processPerms _tmTable _tmSelectPermissions
|
|
|
|
|
withPathK "update_permissions" $ processPerms _tmTable _tmUpdatePermissions
|
|
|
|
|
withPathK "delete_permissions" $ processPerms _tmTable _tmDeletePermissions
|
|
|
|
|
|
|
|
|
|
-- Event triggers
|
|
|
|
|
withPathK "event_triggers" $
|
|
|
|
|
indexedForM_ _tmEventTriggers $ \etc -> addEventTriggerToCatalog _tmTable etc
|
|
|
|
|
|
|
|
|
|
-- sql functions
|
|
|
|
|
withPathK "functions" $ indexedForM_ functions $
|
2021-01-29 08:48:17 +03:00
|
|
|
|
\(FunctionMetadata function config _) -> addFunctionToCatalog function config
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
-- query collections
|
|
|
|
|
systemDefined <- askSystemDefined
|
|
|
|
|
withPathK "query_collections" $
|
|
|
|
|
indexedForM_ collections $ \c -> liftTx $ addCollectionToCatalog c systemDefined
|
|
|
|
|
|
|
|
|
|
-- allow list
|
|
|
|
|
withPathK "allowlist" $ do
|
|
|
|
|
indexedForM_ allowlist $ \(CollectionReq name) ->
|
|
|
|
|
liftTx $ addCollectionToAllowlistCatalog name
|
|
|
|
|
|
|
|
|
|
-- remote schemas
|
|
|
|
|
withPathK "remote_schemas" $
|
|
|
|
|
indexedMapM_ (liftTx . addRemoteSchemaToCatalog) schemas
|
|
|
|
|
|
|
|
|
|
-- custom types
|
|
|
|
|
withPathK "custom_types" $ setCustomTypesInCatalog customTypes
|
|
|
|
|
|
|
|
|
|
-- cron triggers
|
|
|
|
|
withPathK "cron_triggers" $
|
|
|
|
|
indexedForM_ cronTriggers $ \ct -> liftTx $ do
|
|
|
|
|
addCronTriggerToCatalog ct
|
|
|
|
|
|
|
|
|
|
-- actions
|
|
|
|
|
withPathK "actions" $
|
|
|
|
|
indexedForM_ actions $ \action -> do
|
|
|
|
|
let createAction =
|
|
|
|
|
CreateAction (_amName action) (_amDefinition action) (_amComment action)
|
|
|
|
|
addActionToCatalog createAction
|
|
|
|
|
withPathK "permissions" $
|
|
|
|
|
indexedForM_ (_amPermissions action) $ \permission -> do
|
|
|
|
|
let createActionPermission = CreateActionPermission (_amName action)
|
|
|
|
|
(_apmRole permission) Nothing (_apmComment permission)
|
|
|
|
|
addActionPermissionToCatalog createActionPermission
|
|
|
|
|
|
|
|
|
|
where
|
|
|
|
|
processPerms tableName perms = indexedForM_ perms $ \perm -> do
|
2021-04-22 00:44:37 +03:00
|
|
|
|
let pt = permAccToType @('Postgres 'Vanilla) $ getPermAcc1 perm
|
2020-12-08 17:22:31 +03:00
|
|
|
|
systemDefined <- askSystemDefined
|
|
|
|
|
liftTx $ addPermissionToCatalog pt tableName perm systemDefined
|
|
|
|
|
|
|
|
|
|
saveTableToCatalog
|
2021-04-22 00:44:37 +03:00
|
|
|
|
:: (MonadTx m, HasSystemDefined m) => QualifiedTable -> Bool -> TableConfig ('Postgres 'Vanilla) -> m ()
|
2020-12-08 17:22:31 +03:00
|
|
|
|
saveTableToCatalog (QualifiedObject sn tn) isEnum config = do
|
|
|
|
|
systemDefined <- askSystemDefined
|
|
|
|
|
liftTx $ Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO "hdb_catalog"."hdb_table"
|
|
|
|
|
(table_schema, table_name, is_system_defined, is_enum, configuration)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
|
|
|
|] (sn, tn, systemDefined, isEnum, configVal) False
|
|
|
|
|
where
|
|
|
|
|
configVal = Q.AltJ $ toJSON config
|
|
|
|
|
|
|
|
|
|
insertRelationshipToCatalog
|
|
|
|
|
:: (MonadTx m, HasSystemDefined m, ToJSON a)
|
|
|
|
|
=> QualifiedTable
|
|
|
|
|
-> RelType
|
|
|
|
|
-> RelDef a
|
|
|
|
|
-> m ()
|
|
|
|
|
insertRelationshipToCatalog (QualifiedObject schema table) relType (RelDef name using comment) = do
|
|
|
|
|
systemDefined <- askSystemDefined
|
|
|
|
|
let args = (schema, table, name, relTypeToTxt relType, Q.AltJ using, comment, systemDefined)
|
|
|
|
|
liftTx $ Q.unitQE defaultTxErrorHandler query args True
|
|
|
|
|
where
|
|
|
|
|
query = [Q.sql|
|
|
|
|
|
INSERT INTO
|
|
|
|
|
hdb_catalog.hdb_relationship
|
|
|
|
|
(table_schema, table_name, rel_name, rel_type, rel_def, comment, is_system_defined)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5 :: jsonb, $6, $7) |]
|
|
|
|
|
|
|
|
|
|
addEventTriggerToCatalog
|
|
|
|
|
:: MonadTx m
|
|
|
|
|
=> QualifiedTable
|
|
|
|
|
-> EventTriggerConf
|
|
|
|
|
-> m ()
|
|
|
|
|
addEventTriggerToCatalog qt etc = liftTx do
|
|
|
|
|
Q.unitQE defaultTxErrorHandler
|
|
|
|
|
[Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.event_triggers
|
|
|
|
|
(name, type, schema_name, table_name, configuration)
|
|
|
|
|
VALUES ($1, 'table', $2, $3, $4)
|
|
|
|
|
|] (name, sn, tn, Q.AltJ $ toJSON etc) False
|
|
|
|
|
where
|
|
|
|
|
QualifiedObject sn tn = qt
|
|
|
|
|
(EventTriggerConf name _ _ _ _ _) = etc
|
|
|
|
|
|
|
|
|
|
addComputedFieldToCatalog
|
|
|
|
|
:: MonadTx m
|
2021-04-22 00:44:37 +03:00
|
|
|
|
=> AddComputedField ('Postgres 'Vanilla) -> m ()
|
2020-12-08 17:22:31 +03:00
|
|
|
|
addComputedFieldToCatalog q =
|
|
|
|
|
liftTx $ Q.withQE defaultTxErrorHandler
|
|
|
|
|
[Q.sql|
|
|
|
|
|
INSERT INTO hdb_catalog.hdb_computed_field
|
|
|
|
|
(table_schema, table_name, computed_field_name, definition, comment)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
|
|
|
|] (schemaName, tableName, computedField, Q.AltJ definition, comment) True
|
|
|
|
|
where
|
|
|
|
|
QualifiedObject schemaName tableName = table
|
2020-12-28 15:56:00 +03:00
|
|
|
|
AddComputedField _ table computedField definition comment = q
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
2021-04-22 00:44:37 +03:00
|
|
|
|
addRemoteRelationshipToCatalog :: MonadTx m => RemoteRelationship ('Postgres 'Vanilla) -> m ()
|
2020-12-08 17:22:31 +03:00
|
|
|
|
addRemoteRelationshipToCatalog remoteRelationship = liftTx $
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO hdb_catalog.hdb_remote_relationship
|
|
|
|
|
(remote_relationship_name, table_schema, table_name, definition)
|
|
|
|
|
VALUES ($1, $2, $3, $4::jsonb)
|
|
|
|
|
|] (rtrName remoteRelationship, schemaName, tableName, Q.AltJ definition) True
|
|
|
|
|
where
|
|
|
|
|
QualifiedObject schemaName tableName = rtrTable remoteRelationship
|
|
|
|
|
definition = mkRemoteRelationshipDef remoteRelationship
|
|
|
|
|
mkRemoteRelationshipDef RemoteRelationship {..} =
|
|
|
|
|
RemoteRelationshipDef rtrRemoteSchema rtrHasuraFields rtrRemoteField
|
|
|
|
|
|
|
|
|
|
addFunctionToCatalog
|
|
|
|
|
:: (MonadTx m, HasSystemDefined m)
|
|
|
|
|
=> QualifiedFunction -> FunctionConfig -> m ()
|
|
|
|
|
addFunctionToCatalog (QualifiedObject sn fn) config = do
|
|
|
|
|
systemDefined <- askSystemDefined
|
|
|
|
|
liftTx $ Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO "hdb_catalog"."hdb_function"
|
|
|
|
|
(function_schema, function_name, configuration, is_system_defined)
|
|
|
|
|
VALUES ($1, $2, $3, $4)
|
|
|
|
|
|] (sn, fn, Q.AltJ config, systemDefined) False
|
|
|
|
|
|
|
|
|
|
addRemoteSchemaToCatalog
|
2020-12-21 12:11:37 +03:00
|
|
|
|
:: RemoteSchemaMetadata
|
2020-12-08 17:22:31 +03:00
|
|
|
|
-> Q.TxE QErr ()
|
2020-12-21 12:11:37 +03:00
|
|
|
|
addRemoteSchemaToCatalog (RemoteSchemaMetadata name def comment _) =
|
2020-12-08 17:22:31 +03:00
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.remote_schemas
|
|
|
|
|
(name, definition, comment)
|
|
|
|
|
VALUES ($1, $2, $3)
|
|
|
|
|
|] (name, Q.AltJ $ toJSON def, comment) True
|
|
|
|
|
|
|
|
|
|
addCollectionToCatalog
|
|
|
|
|
:: MonadTx m => CreateCollection -> SystemDefined -> m ()
|
|
|
|
|
addCollectionToCatalog (CreateCollection name defn mComment) systemDefined = liftTx $
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO hdb_catalog.hdb_query_collection
|
|
|
|
|
(collection_name, collection_defn, comment, is_system_defined)
|
|
|
|
|
VALUES ($1, $2, $3, $4)
|
|
|
|
|
|] (name, Q.AltJ defn, mComment, systemDefined) True
|
|
|
|
|
|
|
|
|
|
addCollectionToAllowlistCatalog :: MonadTx m => CollectionName -> m ()
|
|
|
|
|
addCollectionToAllowlistCatalog collName = liftTx $
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO hdb_catalog.hdb_allowlist
|
|
|
|
|
(collection_name)
|
|
|
|
|
VALUES ($1)
|
|
|
|
|
|] (Identity collName) True
|
|
|
|
|
|
|
|
|
|
setCustomTypesInCatalog :: MonadTx m => CustomTypes -> m ()
|
|
|
|
|
setCustomTypesInCatalog customTypes = liftTx do
|
|
|
|
|
clearCustomTypes
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.hdb_custom_types
|
|
|
|
|
(custom_types)
|
|
|
|
|
VALUES ($1)
|
|
|
|
|
|] (Identity $ Q.AltJ customTypes) False
|
|
|
|
|
where
|
|
|
|
|
clearCustomTypes = do
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
DELETE FROM hdb_catalog.hdb_custom_types
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
addActionToCatalog :: (MonadTx m) => CreateAction -> m ()
|
|
|
|
|
addActionToCatalog (CreateAction actionName actionDefinition comment) = do
|
|
|
|
|
liftTx $ Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.hdb_action
|
|
|
|
|
(action_name, action_defn, comment)
|
|
|
|
|
VALUES ($1, $2, $3)
|
|
|
|
|
|] (actionName, Q.AltJ actionDefinition, comment) True
|
|
|
|
|
|
|
|
|
|
addActionPermissionToCatalog :: (MonadTx m) => CreateActionPermission -> m ()
|
|
|
|
|
addActionPermissionToCatalog CreateActionPermission{..}= do
|
|
|
|
|
liftTx $ Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.hdb_action_permission
|
|
|
|
|
(action_name, role_name, comment)
|
|
|
|
|
VALUES ($1, $2, $3)
|
|
|
|
|
|] (_capAction, _capRole, _capComment) True
|
|
|
|
|
|
|
|
|
|
addPermissionToCatalog
|
|
|
|
|
:: (ToJSON a, MonadTx m)
|
|
|
|
|
=> PermType
|
|
|
|
|
-> QualifiedTable
|
|
|
|
|
-> PermDef a
|
|
|
|
|
-> SystemDefined
|
|
|
|
|
-> m ()
|
|
|
|
|
addPermissionToCatalog pt (QualifiedObject sn tn) (PermDef rn qdef mComment) systemDefined = liftTx $
|
|
|
|
|
Q.unitQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
INSERT INTO
|
|
|
|
|
hdb_catalog.hdb_permission
|
|
|
|
|
(table_schema, table_name, role_name, perm_type, perm_def, comment, is_system_defined)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5 :: jsonb, $6, $7)
|
|
|
|
|
|] (sn, tn, rn, permTypeToCode pt, Q.AltJ qdef, mComment, systemDefined) True
|
|
|
|
|
|
|
|
|
|
addCronTriggerToCatalog :: (MonadTx m) => CronTriggerMetadata -> m ()
|
|
|
|
|
addCronTriggerToCatalog CronTriggerMetadata {..} = liftTx $ do
|
|
|
|
|
Q.unitQE defaultTxErrorHandler
|
|
|
|
|
[Q.sql|
|
|
|
|
|
INSERT into hdb_catalog.hdb_cron_triggers
|
|
|
|
|
(name, webhook_conf, cron_schedule, payload, retry_conf, header_conf, include_in_metadata, comment)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
|
|
|
|] (ctName, Q.AltJ ctWebhook, ctSchedule, Q.AltJ <$> ctPayload, Q.AltJ ctRetryConf
|
|
|
|
|
,Q.AltJ ctHeaders, ctIncludeInMetadata, ctComment) False
|
|
|
|
|
currentTime <- liftIO C.getCurrentTime
|
|
|
|
|
let scheduleTimes = generateScheduleTimes currentTime 100 ctSchedule -- generate next 100 events
|
|
|
|
|
insertScheduledEventTx $ SESCron $ map (CronEventSeed ctName) scheduleTimes
|
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
fetchMetadataFromHdbTables :: MonadTx m => m MetadataNoSources
|
2020-12-08 17:22:31 +03:00
|
|
|
|
fetchMetadataFromHdbTables = liftTx do
|
|
|
|
|
tables <- Q.catchE defaultTxErrorHandler fetchTables
|
|
|
|
|
let tableMetaMap = OMap.fromList . flip map tables $
|
|
|
|
|
\(schema, name, isEnum, maybeConfig) ->
|
|
|
|
|
let qualifiedName = QualifiedObject schema name
|
|
|
|
|
configuration = maybe emptyTableConfig Q.getAltJ maybeConfig
|
|
|
|
|
in (qualifiedName, mkTableMeta qualifiedName isEnum configuration)
|
|
|
|
|
|
|
|
|
|
-- Fetch all the relationships
|
|
|
|
|
relationships <- Q.catchE defaultTxErrorHandler fetchRelationships
|
|
|
|
|
|
|
|
|
|
objRelDefs <- mkRelDefs ObjRel relationships
|
|
|
|
|
arrRelDefs <- mkRelDefs ArrRel relationships
|
|
|
|
|
|
|
|
|
|
-- Fetch all the permissions
|
|
|
|
|
permissions <- Q.catchE defaultTxErrorHandler fetchPermissions
|
|
|
|
|
|
|
|
|
|
-- Parse all the permissions
|
|
|
|
|
insPermDefs <- mkPermDefs PTInsert permissions
|
|
|
|
|
selPermDefs <- mkPermDefs PTSelect permissions
|
|
|
|
|
updPermDefs <- mkPermDefs PTUpdate permissions
|
|
|
|
|
delPermDefs <- mkPermDefs PTDelete permissions
|
|
|
|
|
|
|
|
|
|
-- Fetch all event triggers
|
|
|
|
|
eventTriggers <- Q.catchE defaultTxErrorHandler fetchEventTriggers
|
|
|
|
|
triggerMetaDefs <- mkTriggerMetaDefs eventTriggers
|
|
|
|
|
|
|
|
|
|
-- Fetch all computed fields
|
|
|
|
|
computedFields <- fetchComputedFields
|
|
|
|
|
|
|
|
|
|
-- Fetch all remote relationships
|
|
|
|
|
remoteRelationships <- Q.catchE defaultTxErrorHandler fetchRemoteRelationships
|
|
|
|
|
|
|
|
|
|
let (_, fullTableMetaMap) = flip runState tableMetaMap $ do
|
|
|
|
|
modMetaMap tmObjectRelationships _rdName objRelDefs
|
|
|
|
|
modMetaMap tmArrayRelationships _rdName arrRelDefs
|
|
|
|
|
modMetaMap tmInsertPermissions _pdRole insPermDefs
|
|
|
|
|
modMetaMap tmSelectPermissions _pdRole selPermDefs
|
|
|
|
|
modMetaMap tmUpdatePermissions _pdRole updPermDefs
|
|
|
|
|
modMetaMap tmDeletePermissions _pdRole delPermDefs
|
|
|
|
|
modMetaMap tmEventTriggers etcName triggerMetaDefs
|
|
|
|
|
modMetaMap tmComputedFields _cfmName computedFields
|
|
|
|
|
modMetaMap tmRemoteRelationships _rrmName remoteRelationships
|
|
|
|
|
|
|
|
|
|
-- fetch all functions
|
|
|
|
|
functions <- Q.catchE defaultTxErrorHandler fetchFunctions
|
|
|
|
|
|
|
|
|
|
-- fetch all remote schemas
|
2020-12-21 12:11:37 +03:00
|
|
|
|
remoteSchemas <- oMapFromL _rsmName <$> fetchRemoteSchemas
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
-- fetch all collections
|
|
|
|
|
collections <- oMapFromL _ccName <$> fetchCollections
|
|
|
|
|
|
|
|
|
|
-- fetch allow list
|
|
|
|
|
allowlist <- HSIns.fromList . map CollectionReq <$> fetchAllowlists
|
|
|
|
|
|
|
|
|
|
customTypes <- fetchCustomTypes
|
|
|
|
|
|
|
|
|
|
-- fetch actions
|
|
|
|
|
actions <- oMapFromL _amName <$> fetchActions
|
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
|
MetadataNoSources fullTableMetaMap functions remoteSchemas collections
|
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
|
|
|
|
allowlist customTypes actions <$> fetchCronTriggers
|
|
|
|
|
|
2020-12-08 17:22:31 +03:00
|
|
|
|
where
|
|
|
|
|
modMetaMap l f xs = do
|
|
|
|
|
st <- get
|
|
|
|
|
put $ foldl' (\b (qt, dfn) -> b & at qt._Just.l %~ OMap.insert (f dfn) dfn) st xs
|
|
|
|
|
|
|
|
|
|
mkPermDefs pt = mapM permRowToDef . filter (\pr -> pr ^. _4 == pt)
|
|
|
|
|
|
|
|
|
|
permRowToDef (sn, tn, rn, _, Q.AltJ pDef, mComment) = do
|
|
|
|
|
perm <- decodeValue pDef
|
|
|
|
|
return (QualifiedObject sn tn, PermDef rn perm mComment)
|
|
|
|
|
|
|
|
|
|
mkRelDefs rt = mapM relRowToDef . filter (\rr -> rr ^. _4 == rt)
|
|
|
|
|
|
|
|
|
|
relRowToDef (sn, tn, rn, _, Q.AltJ rDef, mComment) = do
|
|
|
|
|
using <- decodeValue rDef
|
|
|
|
|
return (QualifiedObject sn tn, RelDef rn using mComment)
|
|
|
|
|
|
|
|
|
|
mkTriggerMetaDefs = mapM trigRowToDef
|
|
|
|
|
|
|
|
|
|
trigRowToDef (sn, tn, Q.AltJ configuration) = do
|
|
|
|
|
conf <- decodeValue configuration
|
|
|
|
|
return (QualifiedObject sn tn, conf::EventTriggerConf)
|
|
|
|
|
|
|
|
|
|
fetchTables =
|
|
|
|
|
Q.listQ [Q.sql|
|
|
|
|
|
SELECT table_schema, table_name, is_enum, configuration::json
|
|
|
|
|
FROM hdb_catalog.hdb_table
|
|
|
|
|
WHERE is_system_defined = 'false'
|
|
|
|
|
ORDER BY table_schema ASC, table_name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
fetchRelationships =
|
|
|
|
|
Q.listQ [Q.sql|
|
|
|
|
|
SELECT table_schema, table_name, rel_name, rel_type, rel_def::json, comment
|
|
|
|
|
FROM hdb_catalog.hdb_relationship
|
|
|
|
|
WHERE is_system_defined = 'false'
|
|
|
|
|
ORDER BY table_schema ASC, table_name ASC, rel_name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
fetchPermissions =
|
|
|
|
|
Q.listQ [Q.sql|
|
|
|
|
|
SELECT table_schema, table_name, role_name, perm_type, perm_def::json, comment
|
|
|
|
|
FROM hdb_catalog.hdb_permission
|
|
|
|
|
WHERE is_system_defined = 'false'
|
|
|
|
|
ORDER BY table_schema ASC, table_name ASC, role_name ASC, perm_type ASC
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
fetchEventTriggers =
|
|
|
|
|
Q.listQ [Q.sql|
|
|
|
|
|
SELECT e.schema_name, e.table_name, e.configuration::json
|
|
|
|
|
FROM hdb_catalog.event_triggers e
|
|
|
|
|
ORDER BY e.schema_name ASC, e.table_name ASC, e.name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
fetchFunctions = do
|
|
|
|
|
l <- Q.listQ [Q.sql|
|
|
|
|
|
SELECT function_schema, function_name, configuration::json
|
|
|
|
|
FROM hdb_catalog.hdb_function
|
|
|
|
|
WHERE is_system_defined = 'false'
|
|
|
|
|
ORDER BY function_schema ASC, function_name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
pure $ oMapFromL _fmFunction $
|
|
|
|
|
flip map l $ \(sn, fn, Q.AltJ config) ->
|
2021-01-29 08:48:17 +03:00
|
|
|
|
-- function permissions were only introduced post 43rd
|
|
|
|
|
-- migration, so it's impossible we get any permissions
|
|
|
|
|
-- here
|
|
|
|
|
FunctionMetadata (QualifiedObject sn fn) config []
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
fetchRemoteSchemas =
|
|
|
|
|
map fromRow <$> Q.listQE defaultTxErrorHandler
|
|
|
|
|
[Q.sql|
|
|
|
|
|
SELECT name, definition, comment
|
|
|
|
|
FROM hdb_catalog.remote_schemas
|
|
|
|
|
ORDER BY name ASC
|
|
|
|
|
|] () True
|
|
|
|
|
where
|
|
|
|
|
fromRow (name, Q.AltJ def, comment) =
|
2020-12-21 12:11:37 +03:00
|
|
|
|
RemoteSchemaMetadata name def comment mempty
|
2020-12-08 17:22:31 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetchCollections =
|
|
|
|
|
map fromRow <$> Q.listQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
SELECT collection_name, collection_defn::json, comment
|
|
|
|
|
FROM hdb_catalog.hdb_query_collection
|
|
|
|
|
WHERE is_system_defined = 'false'
|
|
|
|
|
ORDER BY collection_name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
where
|
|
|
|
|
fromRow (name, Q.AltJ defn, mComment) =
|
|
|
|
|
CreateCollection name defn mComment
|
|
|
|
|
|
|
|
|
|
fetchAllowlists = map runIdentity <$>
|
|
|
|
|
Q.listQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
SELECT collection_name
|
|
|
|
|
FROM hdb_catalog.hdb_allowlist
|
|
|
|
|
ORDER BY collection_name ASC
|
|
|
|
|
|] () False
|
|
|
|
|
|
|
|
|
|
fetchComputedFields = do
|
|
|
|
|
r <- Q.listQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
SELECT table_schema, table_name, computed_field_name,
|
|
|
|
|
definition::json, comment
|
|
|
|
|
FROM hdb_catalog.hdb_computed_field
|
|
|
|
|
|] () False
|
|
|
|
|
pure $ flip map r $ \(schema, table, name, Q.AltJ definition, comment) ->
|
|
|
|
|
( QualifiedObject schema table
|
|
|
|
|
, ComputedFieldMetadata name definition comment
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
fetchCronTriggers =
|
2021-05-21 05:46:58 +03:00
|
|
|
|
oMapFromL ctName . map uncurryCronTrigger
|
2020-12-08 17:22:31 +03:00
|
|
|
|
<$> Q.listQE defaultTxErrorHandler
|
|
|
|
|
[Q.sql|
|
|
|
|
|
SELECT ct.name, ct.webhook_conf, ct.cron_schedule, ct.payload,
|
|
|
|
|
ct.retry_conf, ct.header_conf, ct.include_in_metadata, ct.comment
|
|
|
|
|
FROM hdb_catalog.hdb_cron_triggers ct
|
|
|
|
|
WHERE include_in_metadata
|
|
|
|
|
|] () False
|
|
|
|
|
where
|
|
|
|
|
uncurryCronTrigger
|
|
|
|
|
(name, webhook, schedule, payload, retryConfig, headerConfig, includeMetadata, comment) =
|
|
|
|
|
CronTriggerMetadata
|
|
|
|
|
{ ctName = name,
|
|
|
|
|
ctWebhook = Q.getAltJ webhook,
|
|
|
|
|
ctSchedule = schedule,
|
|
|
|
|
ctPayload = Q.getAltJ <$> payload,
|
|
|
|
|
ctRetryConf = Q.getAltJ retryConfig,
|
|
|
|
|
ctHeaders = Q.getAltJ headerConfig,
|
|
|
|
|
ctIncludeInMetadata = includeMetadata,
|
|
|
|
|
ctComment = comment
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchCustomTypes :: Q.TxE QErr CustomTypes
|
|
|
|
|
fetchCustomTypes =
|
|
|
|
|
Q.getAltJ . runIdentity . Q.getRow <$>
|
|
|
|
|
Q.rawQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
select coalesce((select custom_types::json from hdb_catalog.hdb_custom_types), '{}'::json)
|
|
|
|
|
|] [] False
|
|
|
|
|
|
|
|
|
|
fetchActions =
|
|
|
|
|
Q.getAltJ . runIdentity . Q.getRow <$> Q.rawQE defaultTxErrorHandler [Q.sql|
|
|
|
|
|
select
|
|
|
|
|
coalesce(
|
|
|
|
|
json_agg(
|
|
|
|
|
json_build_object(
|
|
|
|
|
'name', a.action_name,
|
|
|
|
|
'definition', a.action_defn,
|
|
|
|
|
'comment', a.comment,
|
|
|
|
|
'permissions', ap.permissions
|
|
|
|
|
) order by a.action_name asc
|
|
|
|
|
),
|
|
|
|
|
'[]'
|
|
|
|
|
)
|
|
|
|
|
from
|
|
|
|
|
hdb_catalog.hdb_action as a
|
|
|
|
|
left outer join lateral (
|
|
|
|
|
select
|
|
|
|
|
coalesce(
|
|
|
|
|
json_agg(
|
|
|
|
|
json_build_object(
|
|
|
|
|
'role', ap.role_name,
|
|
|
|
|
'comment', ap.comment
|
|
|
|
|
) order by ap.role_name asc
|
|
|
|
|
),
|
|
|
|
|
'[]'
|
|
|
|
|
) as permissions
|
|
|
|
|
from
|
|
|
|
|
hdb_catalog.hdb_action_permission ap
|
|
|
|
|
where
|
|
|
|
|
ap.action_name = a.action_name
|
|
|
|
|
) ap on true;
|
|
|
|
|
|] [] False
|
|
|
|
|
|
|
|
|
|
fetchRemoteRelationships = do
|
|
|
|
|
r <- Q.listQ [Q.sql|
|
|
|
|
|
SELECT table_schema, table_name,
|
|
|
|
|
remote_relationship_name, definition::json
|
|
|
|
|
FROM hdb_catalog.hdb_remote_relationship
|
|
|
|
|
|] () False
|
|
|
|
|
pure $ flip map r $ \(schema, table, name, Q.AltJ definition) ->
|
|
|
|
|
( QualifiedObject schema table
|
|
|
|
|
, RemoteRelationshipMetadata name definition
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
-- | Drops and recreates all “system-defined” metadata, aka metadata for tables and views in the
|
|
|
|
|
-- @information_schema@ and @hdb_catalog@ schemas. These tables and views are tracked to expose them
|
|
|
|
|
-- to the console, which allows us to reuse the same functionality we use to implement user-defined
|
|
|
|
|
-- APIs to expose the catalog.
|
|
|
|
|
--
|
|
|
|
|
-- This process has a long and storied history.
|
|
|
|
|
--
|
|
|
|
|
-- In the past, we reused the same machinery we use for CLI migrations to define our own internal
|
|
|
|
|
-- metadata migrations. This caused trouble, however, as we’d have to run those migrations in
|
|
|
|
|
-- lockstep with our SQL migrations to ensure the two didn’t get out of sync. This in turn caused
|
|
|
|
|
-- trouble because those migrations would hit code paths inside @graphql-engine@ to add or remove
|
|
|
|
|
-- things from the @pg_catalog@ tables, and /that/ in turn would fail because we hadn’t finished
|
|
|
|
|
-- running the SQL migrations, so we were running a new version of the code against an old version
|
|
|
|
|
-- of the schema! That caused #2826.
|
|
|
|
|
--
|
|
|
|
|
-- To fix that, #2379 switched to the approach of just dropping and recreating all system metadata
|
|
|
|
|
-- every time we run any SQL migrations. But /that/ in turn caused trouble due to the way we were
|
|
|
|
|
-- constantly rebuilding the schema cache (#3354), causing us to switch to incremental schema cache
|
|
|
|
|
-- construction (#3394). However, although that mostly resolved the problem, we still weren’t
|
|
|
|
|
-- totally out of the woods, as the incremental construction was still too slow on slow Postgres
|
|
|
|
|
-- instances (#3654).
|
|
|
|
|
--
|
|
|
|
|
-- To sidestep the whole issue, as of #3686 we now just create all the system metadata in code here,
|
|
|
|
|
-- and we only rebuild the schema cache once, at the very end. This is a little unsatisfying, since
|
|
|
|
|
-- it means our internal migrations are “blessed” compared to user-defined CLI migrations. If we
|
|
|
|
|
-- improve CLI migrations further in the future, maybe we can switch back to using that approach,
|
|
|
|
|
-- instead.
|
|
|
|
|
recreateSystemMetadata :: (MonadTx m) => m ()
|
|
|
|
|
recreateSystemMetadata = do
|
2021-03-16 20:35:35 +03:00
|
|
|
|
() <- liftTx $ Q.multiQE defaultTxErrorHandler $(makeRelativeToProject "src-rsr/clear_system_metadata.sql" >>= Q.sqlFromFile)
|
2020-12-08 17:22:31 +03:00
|
|
|
|
runHasSystemDefinedT (SystemDefined True) $ for_ systemMetadata \(tableName, tableRels) -> do
|
|
|
|
|
saveTableToCatalog tableName False emptyTableConfig
|
|
|
|
|
for_ tableRels \case
|
|
|
|
|
Left relDef -> insertRelationshipToCatalog tableName ObjRel relDef
|
|
|
|
|
Right relDef -> insertRelationshipToCatalog tableName ArrRel relDef
|
|
|
|
|
where
|
2021-04-22 00:44:37 +03:00
|
|
|
|
systemMetadata :: [(QualifiedTable, [Either (ObjRelDef ('Postgres 'Vanilla)) (ArrRelDef ('Postgres 'Vanilla))])]
|
2020-12-08 17:22:31 +03:00
|
|
|
|
systemMetadata =
|
|
|
|
|
[ table "information_schema" "tables" []
|
|
|
|
|
, table "information_schema" "schemata" []
|
|
|
|
|
, table "information_schema" "views" []
|
|
|
|
|
, table "information_schema" "columns" []
|
|
|
|
|
, table "hdb_catalog" "hdb_table"
|
|
|
|
|
[ objectRel $$(nonEmptyText "detail") $
|
|
|
|
|
manualConfig "information_schema" "tables" tableNameMapping
|
|
|
|
|
, objectRel $$(nonEmptyText "primary_key") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_primary_key" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "columns") $
|
|
|
|
|
manualConfig "information_schema" "columns" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "foreign_key_constraints") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_foreign_key_constraint" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "relationships") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_relationship" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "permissions") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_permission_agg" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "computed_fields") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_computed_field" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "check_constraints") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_check_constraint" tableNameMapping
|
|
|
|
|
, arrayRel $$(nonEmptyText "unique_constraints") $
|
|
|
|
|
manualConfig "hdb_catalog" "hdb_unique_constraint" tableNameMapping
|
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_primary_key" []
|
|
|
|
|
, table "hdb_catalog" "hdb_foreign_key_constraint" []
|
|
|
|
|
, table "hdb_catalog" "hdb_relationship" []
|
|
|
|
|
, table "hdb_catalog" "hdb_permission_agg" []
|
|
|
|
|
, table "hdb_catalog" "hdb_computed_field" []
|
|
|
|
|
, table "hdb_catalog" "hdb_check_constraint" []
|
|
|
|
|
, table "hdb_catalog" "hdb_unique_constraint" []
|
|
|
|
|
, table "hdb_catalog" "hdb_remote_relationship" []
|
|
|
|
|
, table "hdb_catalog" "event_triggers"
|
|
|
|
|
[ arrayRel $$(nonEmptyText "events") $
|
|
|
|
|
manualConfig "hdb_catalog" "event_log" [("name", "trigger_name")] ]
|
|
|
|
|
, table "hdb_catalog" "event_log"
|
|
|
|
|
[ objectRel $$(nonEmptyText "trigger") $
|
|
|
|
|
manualConfig "hdb_catalog" "event_triggers" [("trigger_name", "name")]
|
|
|
|
|
, arrayRel $$(nonEmptyText "logs") $ RUFKeyOn $
|
2021-05-21 05:46:58 +03:00
|
|
|
|
ArrRelUsingFKeyOn (QualifiedObject "hdb_catalog" "event_invocation_logs") (pure "event_id") ]
|
2020-12-08 17:22:31 +03:00
|
|
|
|
, table "hdb_catalog" "event_invocation_logs"
|
2021-05-21 05:46:58 +03:00
|
|
|
|
[ objectRel $$(nonEmptyText "event") $ RUFKeyOn $ SameTable (pure "event_id") ]
|
2020-12-08 17:22:31 +03:00
|
|
|
|
, table "hdb_catalog" "hdb_function" []
|
|
|
|
|
, table "hdb_catalog" "hdb_function_agg"
|
|
|
|
|
[ objectRel $$(nonEmptyText "return_table_info") $ manualConfig "hdb_catalog" "hdb_table"
|
|
|
|
|
[ ("return_type_schema", "table_schema")
|
|
|
|
|
, ("return_type_name", "table_name") ] ]
|
|
|
|
|
, table "hdb_catalog" "remote_schemas" []
|
|
|
|
|
, table "hdb_catalog" "hdb_version" []
|
|
|
|
|
, table "hdb_catalog" "hdb_query_collection" []
|
|
|
|
|
, table "hdb_catalog" "hdb_allowlist" []
|
|
|
|
|
, table "hdb_catalog" "hdb_custom_types" []
|
|
|
|
|
, table "hdb_catalog" "hdb_action_permission" []
|
|
|
|
|
, table "hdb_catalog" "hdb_action"
|
|
|
|
|
[ arrayRel $$(nonEmptyText "permissions") $ manualConfig "hdb_catalog" "hdb_action_permission"
|
|
|
|
|
[("action_name", "action_name")]
|
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_action_log" []
|
|
|
|
|
, table "hdb_catalog" "hdb_role"
|
|
|
|
|
[ arrayRel $$(nonEmptyText "action_permissions") $ manualConfig "hdb_catalog" "hdb_action_permission"
|
|
|
|
|
[("role_name", "role_name")]
|
|
|
|
|
, arrayRel $$(nonEmptyText "permissions") $ manualConfig "hdb_catalog" "hdb_permission_agg"
|
|
|
|
|
[("role_name", "role_name")]
|
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_cron_triggers"
|
|
|
|
|
[ arrayRel $$(nonEmptyText "cron_events") $ RUFKeyOn $
|
2021-05-21 05:46:58 +03:00
|
|
|
|
ArrRelUsingFKeyOn (QualifiedObject "hdb_catalog" "hdb_cron_events") (pure "trigger_name")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_cron_events"
|
2021-05-21 05:46:58 +03:00
|
|
|
|
[ objectRel $$(nonEmptyText "cron_trigger") $ RUFKeyOn $ SameTable (pure "trigger_name")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
, arrayRel $$(nonEmptyText "cron_event_logs") $ RUFKeyOn $
|
2021-05-21 05:46:58 +03:00
|
|
|
|
ArrRelUsingFKeyOn (QualifiedObject "hdb_catalog" "hdb_cron_event_invocation_logs") (pure "event_id")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_cron_event_invocation_logs"
|
2021-05-21 05:46:58 +03:00
|
|
|
|
[ objectRel $$(nonEmptyText "cron_event") $ RUFKeyOn $ SameTable (pure "event_id")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_scheduled_events"
|
|
|
|
|
[ arrayRel $$(nonEmptyText "scheduled_event_logs") $ RUFKeyOn $
|
2021-05-21 05:46:58 +03:00
|
|
|
|
ArrRelUsingFKeyOn (QualifiedObject "hdb_catalog" "hdb_scheduled_event_invocation_logs") (pure "event_id")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
]
|
|
|
|
|
, table "hdb_catalog" "hdb_scheduled_event_invocation_logs"
|
2021-05-21 05:46:58 +03:00
|
|
|
|
[ objectRel $$(nonEmptyText "scheduled_event") $ RUFKeyOn $ SameTable (pure "event_id")
|
2020-12-08 17:22:31 +03:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
tableNameMapping =
|
|
|
|
|
[ ("table_schema", "table_schema")
|
|
|
|
|
, ("table_name", "table_name") ]
|
|
|
|
|
|
|
|
|
|
table schemaName tableName relationships = (QualifiedObject schemaName tableName, relationships)
|
|
|
|
|
objectRel name using = Left $ RelDef (RelName name) using Nothing
|
|
|
|
|
arrayRel name using = Right $ RelDef (RelName name) using Nothing
|
|
|
|
|
manualConfig schemaName tableName columns =
|
2021-03-03 16:02:00 +03:00
|
|
|
|
RUManual $ RelManualConfig (QualifiedObject schemaName tableName) (HM.fromList columns) Nothing
|