server: remove the extraneous drop function while creating an event trigger

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3956
GitOrigin-RevId: 6d97ffce00fe17ca7f15a8e74acbc1b3d974f7b4
This commit is contained in:
Karthikeyan Chinnakonda 2022-03-15 14:11:03 +05:30 committed by hasura-bot
parent 69501b2657
commit c6a7f4c488
6 changed files with 94 additions and 24 deletions

View File

@ -9,6 +9,7 @@ module Hasura.Backends.Postgres.DDL.EventTrigger
dropTriggerAndArchiveEvents,
createTableEventTrigger,
dropTriggerQ,
dropDanglingSQLTrigger,
mkAllTriggersQ,
getMaintenanceModeVersion,
fetchUndeliveredEvents,
@ -189,12 +190,24 @@ createTableEventTrigger ::
Maybe (PrimaryKey ('Postgres pgKind) (ColumnInfo ('Postgres pgKind))) ->
m (Either QErr ())
createTableEventTrigger serverConfigCtx sourceConfig table columns triggerName opsDefinition _ = runPgSourceWriteTx sourceConfig $ do
-- Clean all existing triggers
liftTx $ dropTriggerQ triggerName -- executes DROP IF EXISTS.. sql
-- Create the given triggers
flip runReaderT serverConfigCtx $
mkAllTriggersQ triggerName table columns opsDefinition
dropDanglingSQLTrigger ::
( MonadIO m,
MonadError QErr m
) =>
SourceConfig ('Postgres pgKind) ->
TriggerName ->
HashSet Ops ->
m ()
dropDanglingSQLTrigger sourceConfig triggerName ops =
liftEitherM $
liftIO $
runPgSourceWriteTx sourceConfig $
traverse_ (dropTriggerOp triggerName) ops
updateColumnInEventTrigger ::
QualifiedTable ->
PGCol ->
@ -464,21 +477,21 @@ setRetryTx e time = \case
dropTriggerQ :: TriggerName -> Q.TxE QErr ()
dropTriggerQ trn =
mapM_
( \op ->
Q.unitQE
defaultTxErrorHandler
(Q.fromText $ getDropFuncSql op)
()
False
)
[INSERT, UPDATE, DELETE]
mapM_ (dropTriggerOp trn) [INSERT, UPDATE, DELETE]
dropTriggerOp :: TriggerName -> Ops -> Q.TxE QErr ()
dropTriggerOp triggerName triggerOp =
Q.unitQE
defaultTxErrorHandler
(Q.fromText $ getDropFuncSql triggerOp)
()
False
where
getDropFuncSql :: Ops -> Text
getDropFuncSql op =
"DROP FUNCTION IF EXISTS"
<> " hdb_catalog."
<> pgIdenTrigger op trn
<> pgIdenTrigger op triggerName
<> "()"
<> " CASCADE"

View File

@ -35,6 +35,7 @@ import Data.ByteString.Lazy qualified as LBS
import Data.Environment qualified as Env
import Data.HashMap.Strict qualified as HM
import Data.HashMap.Strict.InsOrd qualified as OMap
import Data.HashSet qualified as Set
import Data.Text qualified as T
import Data.Text.Extended
import Hasura.Base.Error
@ -146,7 +147,7 @@ resolveEventTriggerQuery ::
forall b m.
(Backend b, UserInfoM m, QErrM m, CacheRM m) =>
CreateEventTriggerQuery b ->
m (TableCoreInfo b, Bool, EventTriggerConf b)
m (Bool, EventTriggerConf b)
resolveEventTriggerQuery (CreateEventTriggerQuery source name qt insert update delete enableManual retryConf webhook webhookFromEnv mheaders replace reqTransform respTransform) = do
ti <- askTableCoreInfo source qt
-- can only replace for same table
@ -159,20 +160,31 @@ resolveEventTriggerQuery (CreateEventTriggerQuery source name qt insert update d
assertCols ti delete
let rconf = fromMaybe defaultRetryConf retryConf
return (ti, replace, EventTriggerConf name (TriggerOpsDef insert update delete enableManual) webhook webhookFromEnv rconf mheaders reqTransform respTransform)
return (replace, EventTriggerConf name (TriggerOpsDef insert update delete enableManual) webhook webhookFromEnv rconf mheaders reqTransform respTransform)
where
assertCols :: TableCoreInfo b -> Maybe (SubscribeOpSpec b) -> m ()
assertCols ti opSpec = onJust opSpec \sos -> case sosColumns sos of
SubCStar -> return ()
SubCArray columns -> forM_ columns (assertColumnExists @b (_tciFieldInfoMap ti) "")
droppedTriggerOps :: TriggerOpsDef b -> TriggerOpsDef b -> HashSet Ops
droppedTriggerOps oldEventTriggerOps newEventTriggerOps =
Set.fromList $
catMaybes $
[ (bool Nothing (Just INSERT) (isDroppedOp (tdInsert oldEventTriggerOps) (tdInsert newEventTriggerOps))),
(bool Nothing (Just UPDATE) (isDroppedOp (tdUpdate oldEventTriggerOps) (tdUpdate newEventTriggerOps))),
(bool Nothing (Just DELETE) (isDroppedOp (tdDelete oldEventTriggerOps) (tdDelete newEventTriggerOps)))
]
where
isDroppedOp old new = isJust old && isNothing new
createEventTriggerQueryMetadata ::
forall b m.
(BackendMetadata b, QErrM m, UserInfoM m, CacheRWM m, MetadataM m) =>
(BackendMetadata b, QErrM m, UserInfoM m, CacheRWM m, MetadataM m, BackendEventTrigger b, MonadIO m) =>
CreateEventTriggerQuery b ->
m (TableCoreInfo b, EventTriggerConf b)
m ()
createEventTriggerQueryMetadata q = do
(tableCoreInfo, replace, triggerConf) <- resolveEventTriggerQuery q
(replace, triggerConf) <- resolveEventTriggerQuery q
let table = _cetqTable q
source = _cetqSource q
triggerName = etcName triggerConf
@ -181,21 +193,26 @@ createEventTriggerQueryMetadata q = do
AB.mkAnyBackend $
SMOTableObj @b table $
MTOTrigger triggerName
sourceInfo <- askSourceInfo @b source
when replace $ do
existingEventTriggerOps <- etiOpsDef <$> askEventTriggerInfo @b source triggerName
let droppedOps = droppedTriggerOps existingEventTriggerOps (etcDefinition triggerConf)
dropDanglingSQLTrigger @b (_siConfiguration sourceInfo) triggerName droppedOps
buildSchemaCacheFor metadataObj $
MetadataModifier $
tableMetadataSetter @b source table . tmEventTriggers
%~ if replace
then ix triggerName .~ triggerConf
else OMap.insert triggerName triggerConf
pure (tableCoreInfo, triggerConf)
runCreateEventTriggerQuery ::
forall b m.
(BackendMetadata b, QErrM m, UserInfoM m, CacheRWM m, MetadataM m) =>
(BackendMetadata b, BackendEventTrigger b, QErrM m, UserInfoM m, CacheRWM m, MetadataM m, MonadIO m) =>
CreateEventTriggerQuery b ->
m EncJSON
runCreateEventTriggerQuery q = do
void $ createEventTriggerQueryMetadata @b q
createEventTriggerQueryMetadata @b q
pure successMsg
runDeleteEventTriggerQuery ::

View File

@ -30,6 +30,7 @@ import Data.Has (Has, getter)
import Data.HashMap.Strict qualified as Map
import Data.HashMap.Strict.InsOrd.Extended qualified as OMap
import Data.HashSet qualified as HS
import Data.HashSet qualified as Set
import Data.List qualified as L
import Data.TByteString qualified as TBS
import Data.Text qualified as T
@ -58,6 +59,7 @@ import Hasura.RQL.DDL.Webhook.Transform
import Hasura.RQL.DDL.Webhook.Transform.Class (mkReqTransformCtx)
import Hasura.RQL.Types
import Hasura.RQL.Types.Endpoint
import Hasura.RQL.Types.EventTrigger qualified as ET
import Hasura.RQL.Types.Eventing.Backend (BackendEventTrigger (..))
import Hasura.SQL.AnyBackend qualified as AB
import Network.HTTP.Client.Transformable qualified as HTTP
@ -279,7 +281,8 @@ runReplaceMetadataV2 ReplaceMetadataV2 {..} = do
dispatch oldBackendSourceMetadata \oldSourceMetadata -> do
let oldTriggersMap = getTriggersMap oldSourceMetadata
newTriggersMap = getTriggersMap newSourceMetadata
droppedTriggers = OMap.keys $ oldTriggersMap `OMap.difference` newTriggersMap
droppedEventTriggers = OMap.keys $ oldTriggersMap `OMap.difference` newTriggersMap
retainedNewTriggers = newTriggersMap `OMap.intersection` oldTriggersMap
catcher e@QErr {qeCode}
| qeCode == Unexpected = pure () -- NOTE: This information should be returned by the inconsistent_metadata response, so doesn't need additional logging.
| otherwise = throwError e -- rethrow other errors
@ -292,7 +295,20 @@ runReplaceMetadataV2 ReplaceMetadataV2 {..} = do
return $
flip catchError catcher do
sourceConfig <- askSourceConfig @b source
for_ droppedTriggers $ dropTriggerAndArchiveEvents @b sourceConfig
for_ droppedEventTriggers $ dropTriggerAndArchiveEvents @b sourceConfig
for_ (OMap.toList retainedNewTriggers) $ \(retainedNewTriggerName, retainedNewTriggerConf) ->
case OMap.lookup retainedNewTriggerName oldTriggersMap of
Nothing -> pure ()
Just oldTriggerConf -> do
let newTriggerOps = etcDefinition retainedNewTriggerConf
oldTriggerOps = etcDefinition oldTriggerConf
isDroppedOp old new = isJust old && isNothing new
droppedOps =
[ (bool Nothing (Just INSERT) (isDroppedOp (tdInsert oldTriggerOps) (tdInsert newTriggerOps))),
(bool Nothing (Just UPDATE) (isDroppedOp (tdUpdate oldTriggerOps) (tdUpdate newTriggerOps))),
(bool Nothing (Just ET.DELETE) (isDroppedOp (tdDelete oldTriggerOps) (tdDelete newTriggerOps)))
]
dropDanglingSQLTrigger @b sourceConfig retainedNewTriggerName (Set.fromList $ catMaybes droppedOps)
where
getTriggersMap = OMap.unions . map _tmEventTriggers . OMap.elems . _smTables

View File

@ -63,7 +63,9 @@ newtype TriggerName = TriggerName {unTriggerName :: NonEmptyText}
triggerNameToTxt :: TriggerName -> Text
triggerNameToTxt = unNonEmptyText . unTriggerName
data Ops = INSERT | UPDATE | DELETE | MANUAL deriving (Show)
data Ops = INSERT | UPDATE | DELETE | MANUAL deriving (Show, Eq, Generic)
instance Hashable Ops
data SubscribeColumns (b :: BackendType) = SubCStar | SubCArray [Column b]
deriving (Generic)

View File

@ -150,6 +150,21 @@ class Backend b => BackendEventTrigger (b :: BackendType) where
TriggerName ->
m ()
-- | @dropDanglingSQLTriggger@ is used to delete the extraneous SQL triggers created
-- by an event trigger. The extraneous SQL triggers can be created when
-- an event trigger's definition is replaced to a new definition. For example,
-- an event trigger `authors_all` had an INSERT and UPDATE trigger defined
-- earlier and after it has UPDATE and DELETE triggers. So, in this case, we need
-- to drop the trigger created by us earlier for the INSERT trigger.
dropDanglingSQLTrigger ::
( MonadIO m,
MonadError QErr m
) =>
SourceConfig b ->
TriggerName ->
HashSet Ops ->
m ()
redeliverEvent ::
(MonadIO m, MonadError QErr m) =>
SourceConfig b ->
@ -192,6 +207,7 @@ instance BackendEventTrigger ('Postgres 'Vanilla) where
recordError = PG.recordError
recordError' = PG.recordError'
dropTriggerAndArchiveEvents = PG.dropTriggerAndArchiveEvents
dropDanglingSQLTrigger = PG.dropDanglingSQLTrigger
redeliverEvent = PG.redeliverEvent
unlockEventsInSource = PG.unlockEventsInSource
createTableEventTrigger = PG.createTableEventTrigger
@ -205,6 +221,7 @@ instance BackendEventTrigger ('Postgres 'Citus) where
recordError _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for Citus sources"
recordError' _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for Citus sources"
dropTriggerAndArchiveEvents _ _ = throw400 NotSupported "Event triggers are not supported for Citus sources"
dropDanglingSQLTrigger _ _ _ = throw400 NotSupported "Event triggers are not supported for Citus sources"
redeliverEvent _ _ = throw400 NotSupported "Event triggers are not supported for Citus sources"
unlockEventsInSource _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for Citus sources"
createTableEventTrigger _ _ _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for Citus sources"
@ -218,6 +235,7 @@ instance BackendEventTrigger 'MSSQL where
recordError _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
recordError' _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
dropTriggerAndArchiveEvents _ _ = throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
dropDanglingSQLTrigger _ _ _ = throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
redeliverEvent _ _ = throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
unlockEventsInSource _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MS-SQL sources"
createTableEventTrigger = MSSQL.createTableEventTrigger
@ -231,6 +249,7 @@ instance BackendEventTrigger 'BigQuery where
recordError _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for BigQuery sources"
recordError' _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for BigQuery sources"
dropTriggerAndArchiveEvents _ _ = throw400 NotSupported "Event triggers are not supported for BigQuery sources"
dropDanglingSQLTrigger _ _ _ = throw400 NotSupported "Event triggers are not supported for BigQuery sources"
redeliverEvent _ _ = throw400 NotSupported "Event triggers are not supported for BigQuery sources"
unlockEventsInSource _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for BigQuery sources"
createTableEventTrigger _ _ _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for BigQuery sources"
@ -244,6 +263,7 @@ instance BackendEventTrigger 'MySQL where
recordError _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MySQL sources"
recordError' _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MySQL sources"
dropTriggerAndArchiveEvents _ _ = throw400 NotSupported "Event triggers are not supported for MySQL sources"
dropDanglingSQLTrigger _ _ _ = throw400 NotSupported "Event triggers are not supported for MySQL sources"
redeliverEvent _ _ = throw400 NotSupported "Event triggers are not supported for MySQL sources"
unlockEventsInSource _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MySQL sources"
createTableEventTrigger _ _ _ _ _ _ _ = runExceptT $ throw400 NotSupported "Event triggers are not supported for MySQL sources"
@ -271,6 +291,8 @@ instance BackendEventTrigger 'DataWrapper where
runExceptT $ throw400 NotSupported "Event triggers are not supported for GraphQL Data Wrappers."
dropTriggerAndArchiveEvents _ _ =
throw400 NotSupported "Event triggers are not supported for GraphQL Data Wrappers."
dropDanglingSQLTrigger _ _ _ =
throw400 NotSupported "Event triggers are not supported for GraphQL Data Wrappers"
redeliverEvent _ _ =
throw400 NotSupported "Event triggers are not supported for GraphQL Data Wrappers."
unlockEventsInSource _ _ =

View File

@ -433,7 +433,7 @@ runMetadataQueryV1M env currentResourceVersion = \case
RMAddComputedField q -> runAddComputedField q
RMDropComputedField q -> runDropComputedField q
RMCreateEventTrigger q ->
dispatchMetadata
dispatchMetadataAndEventTrigger
( validateTransforms
(unUnvalidate1 . cetqRequestTransform . _Just)
(runCreateEventTriggerQuery . _unUnvalidate1)