diff --git a/packages/tools/src/DatabaseAnalyser.ts b/packages/tools/src/DatabaseAnalyser.ts index b0e622616..31c86d93a 100644 --- a/packages/tools/src/DatabaseAnalyser.ts +++ b/packages/tools/src/DatabaseAnalyser.ts @@ -10,7 +10,16 @@ import { extractErrorLogData } from './stringTools'; const logger = getLogger('dbAnalyser'); -const STRUCTURE_FIELDS = ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers']; +const STRUCTURE_FIELDS = [ + 'tables', + 'collections', + 'views', + 'matviews', + 'functions', + 'procedures', + 'triggers', + 'schedulerEvents', +]; const fp_pick = arg => array => _pick(array, arg); @@ -70,7 +79,9 @@ export class DatabaseAnalyser { } async fullAnalysis() { - logger.debug(`Performing full analysis, DB=${dbNameLogCategory(this.dbhan.database)}, engine=${this.driver.engine}`); + logger.debug( + `Performing full analysis, DB=${dbNameLogCategory(this.dbhan.database)}, engine=${this.driver.engine}` + ); const res = this.addEngineField(await this._runAnalysis()); // console.log('FULL ANALYSIS', res); return res; @@ -255,6 +266,7 @@ export class DatabaseAnalyser { ...this.getDeletedObjectsForField(snapshot, 'procedures'), ...this.getDeletedObjectsForField(snapshot, 'functions'), ...this.getDeletedObjectsForField(snapshot, 'triggers'), + ...this.getDeletedObjectsForField(snapshot, 'schedulerEvents'), ]; } @@ -355,6 +367,7 @@ export class DatabaseAnalyser { functions: [], procedures: [], triggers: [], + schedulerEvents: [], }; } diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 66529f00b..0ef99a779 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -157,6 +157,20 @@ export interface TriggerInfo extends SqlObjectInfo { eventType?: 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE'; } +export interface SchedulerEventInfo extends SqlObjectInfo { + definer: string; + eventType: 'RECURRING' | 'ONE TIME'; + onCompletion: 'PRESERVE' | 'NOT PRESERVE'; + status: 'ENABLED' | 'DISABLED'; + lastExecuted?: string; + intervalValue: number; + intervalField: string; + starts: string; + executeAt: string; + enableSql: string; + disableSql: string; +} + export interface SchemaInfo { objectId?: string; schemaName: string; @@ -171,6 +185,7 @@ export interface DatabaseInfoObjects { procedures: ProcedureInfo[]; functions: FunctionInfo[]; triggers: TriggerInfo[]; + schedulerEvents: SchedulerEventInfo[]; } export interface DatabaseInfo extends DatabaseInfoObjects { diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index d904b3654..1e7ae1b42 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -164,6 +164,9 @@ class Analyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Loading triggers' }); const triggers = await this.analyserQuery('triggers'); + this.feedback({ analysingMessage: 'Loading scehduler events' }); + const schedulerEvents = await this.analyserQuery('schedulerEvents'); + const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); this.feedback({ analysingMessage: 'Finalizing DB structure' }); @@ -249,6 +252,23 @@ class Analyser extends DatabaseAnalyser { tableName: row.tableName, createSql: `CREATE TRIGGER ${row.triggerName} ${row.triggerTiming} ${row.eventType} ON ${row.tableName} FOR EACH ROW ${row.definition}`, })), + schedulerEvents: schedulerEvents.rows.map(row => ({ + contentHash: _.isDate(row.LAST_ALTERED) ? row.LAST_ALTERED.toISOString() : row.LAST_ALTERED, + pureName: row.EVENT_NAME, + createSql: row.CREATE_SQL, + enableSql: row.ENABLE_SQL, + disableSql: row.DISABLE_SQL, + objectId: row.EVENT_NAME, + intervalValue: row.INTERVAL_VALUE, + intervalField: row.INTERVAL_FIELD, + starts: row.STARTS, + status: row.STATUS, + executeAt: row.EXECUTE_AT, + lastExecuted: row.LAST_EXECUTED, + eventType: row.EVENT_TYPE, + definer: row.DEFINER, + objectTypeField: 'schedulerEvents', + })), }; this.feedback({ analysingMessage: null }); return res; @@ -258,6 +278,7 @@ class Analyser extends DatabaseAnalyser { const tableModificationsQueryData = await this.analyserQuery('tableModifications'); const procedureModificationsQueryData = await this.analyserQuery('procedureModifications'); const functionModificationsQueryData = await this.analyserQuery('functionModifications'); + const schedulerEvents = await this.analyserQuery('schedulerEvents'); return { tables: tableModificationsQueryData.rows @@ -285,6 +306,23 @@ class Analyser extends DatabaseAnalyser { objectId: x.Name, pureName: x.Name, })), + schedulerEvents: schedulerEvents.rows.map(row => ({ + contentHash: _.isDate(row.LAST_ALTERED) ? row.LAST_ALTERED.toISOString() : row.LAST_ALTERED, + pureName: row.EVENT_NAME, + createSql: row.CREATE_SQL, + enableSql: row.ENABLE_SQL, + disableSql: row.DISABLE_SQL, + objectId: row.EVENT_NAME, + intervalValue: row.INTERVAL_VALUE, + intervalField: row.INTERVAL_FIELD, + starts: row.STARTS, + status: row.STATUS, + executeAt: row.EXECUTE_AT, + lastExecuted: row.LAST_EXECUTED, + eventType: row.EVENT_TYPE, + definer: row.DEFINER, + objectTypeField: 'schedulerEvents', + })), }; } } diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/index.js b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js index 1a7eda8d7..ab23eeaca 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js @@ -12,6 +12,7 @@ const uniqueNames = require('./uniqueNames'); const viewTexts = require('./viewTexts'); const parameters = require('./parameters'); const triggers = require('./triggers'); +const schedulerEvents = require('./schedulerEvents.js'); module.exports = { columns, @@ -28,4 +29,5 @@ module.exports = { uniqueNames, viewTexts, triggers, + schedulerEvents, }; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/schedulerEvents.js b/plugins/dbgate-plugin-mysql/src/backend/sql/schedulerEvents.js new file mode 100644 index 000000000..9558eb70f --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/schedulerEvents.js @@ -0,0 +1,34 @@ +module.exports = ` +SELECT + EVENT_SCHEMA, + EVENT_NAME, + DEFINER, + EVENT_TYPE, + EXECUTE_AT, + INTERVAL_VALUE, + INTERVAL_FIELD, + CREATED, + LAST_EXECUTED, + LAST_ALTERED, + STARTS, + ENDS, + STATUS, + ON_COMPLETION, + CONCAT( + 'CREATE EVENT ', EVENT_NAME, ' ', + CASE WHEN EVENT_TYPE = 'RECURRING' THEN + 'ON SCHEDULE EVERY ' + ELSE 'ON SCHEDULE AT ' + END, + CASE WHEN EVENT_TYPE = 'RECURRING' THEN + CONCAT(INTERVAL_VALUE, ' ', INTERVAL_FIELD) + ELSE DATE_FORMAT(EXECUTE_AT, '%Y-%m-%d %H:%i:%s') + END, + ' DO ', + EVENT_DEFINITION + ) AS CREATE_SQL, + CONCAT('ALTER EVENT ', EVENT_NAME, ' DISABLE;') AS DISABLE_SQL, + CONCAT('ALTER EVENT ', EVENT_NAME, ' ENABLE;') AS ENABLE_SQL +FROM INFORMATION_SCHEMA.EVENTS +WHERE EVENT_SCHEMA = '#DATABASE#' AND EVENT_NAME =OBJECT_ID_CONDITION +`;