From 4cc0a66a7d040da25e1176e0ab757df45a981398 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 12 Dec 2024 18:12:10 +0100 Subject: [PATCH 01/23] feat: add pssql triggers to analyser --- packages/types/dbinfo.d.ts | 10 +++++- .../src/backend/Analyser.js | 14 ++++++++ .../src/backend/sql/index.js | 3 ++ .../src/backend/sql/triggers.js | 36 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 3a50fbbfd..0cf0794b4 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -136,7 +136,15 @@ export interface FunctionInfo extends CallableObjectInfo { returnType?: string; } -export interface TriggerInfo extends SqlObjectInfo {} +export interface TriggerInfo extends SqlObjectInfo { + objectId: string; + trigerName: string; + functionName?: string; + tableName: string; + triggerTiming?: 'BEFORE' | 'AFTER' | 'INSTEAD OF' | null; + triggerLevel?: 'ROW' | 'STATEMENT'; + eventType?: 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE'; +} export interface SchemaInfo { objectId?: string; diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js index 2823ade51..428fef847 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js @@ -241,6 +241,8 @@ class Analyser extends DatabaseAnalyser { return acc; }, {}); + const triggers = await this.analyserQuery('triggers'); + const res = { tables: tables.rows.map(table => { const newTable = { @@ -348,6 +350,18 @@ class Analyser extends DatabaseAnalyser { parameters: functionNameToParameters[`${func.schema_name}.${func.pure_name}`], returnType: func.data_type, })), + triggers: triggers.rows.map(row => ({ + trigerName: row.trigger_name, + functionName: row.function_name, + triggerTiming: row.trigger_timing, + triggerLevel: row.trigger_level, + eventType: row.event_type, + schemaName: row.schema_name, + tableName: row.table_name, + createSql: row.definition, + contentHash: `triggers:${row.trigger_id}`, + objectId: `triggers:${row.trigger_id}`, + })), }; this.feedback({ analysingMessage: null }); diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js index e57203fe9..8f648c64f 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js @@ -16,6 +16,8 @@ const geometryColumns = require('./geometryColumns'); const geographyColumns = require('./geographyColumns'); const proceduresParameters = require('./proceduresParameters'); const foreignKeys = require('./foreignKeys'); +const triggers = require('./triggers'); + const fk_keyColumnUsage = require('./fk_key_column_usage'); module.exports = { @@ -38,4 +40,5 @@ module.exports = { geometryColumns, geographyColumns, proceduresParameters, + triggers, }; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js new file mode 100644 index 000000000..6b8cdd781 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js @@ -0,0 +1,36 @@ +module.exports = ` +SELECT + t.oid AS trigger_id, + t.tgname AS trigger_name, + n.nspname AS schema_name, + c.relname AS table_name, + p.proname AS function_name, + t.tgtype AS original_tgtype, + CASE + WHEN t.tgtype & 1 = 1 THEN 'ROW' + ELSE 'STATEMENT' + END AS trigger_level, + CASE + WHEN tgtype & 2 = 2 THEN 'AFTER' + WHEN tgtype & 1 = 1 THEN 'BEFORE' + ELSE NULL + END AS trigger_timing, + CASE + WHEN tgtype & 32 = 32 THEN 'TRUNCATE' + WHEN tgtype & 16 = 16 THEN 'UPDATE' + WHEN tgtype & 8 = 8 THEN 'DELETE' + WHEN tgtype & 4 = 4 THEN 'INSERT' + ELSE NULL + END AS event_type, + pg_get_triggerdef(t.oid) AS definition +FROM + pg_trigger t +JOIN + pg_class c ON c.oid = t.tgrelid +JOIN + pg_namespace n ON n.oid = c.relnamespace +JOIN + pg_proc p ON p.oid = t.tgfoid +WHERE + NOT t.tgisinternal AND n.nspname =SCHEMA_NAME_CONDITION +`; From 62daa13e5491d444c40287007ca57a55e508be1d Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 12 Dec 2024 19:05:02 +0100 Subject: [PATCH 02/23] feat: add mssql triggers to analyser --- .../src/backend/MsSqlAnalyser.js | 14 ++++++++++ .../src/backend/sql/index.js | 2 ++ .../src/backend/sql/triggers.js | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js index 79b040f76..84b8168c3 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -214,12 +214,26 @@ class MsSqlAnalyser extends DatabaseAnalyser { parameters: functionToParameters[row.objectId], })); + const triggerRows = await this.analyserQuery('triggers'); + + const triggers = triggerRows.rows.map(row => ({ + objectId: `triggers:${row.objectId}`, + contentHash: row.modifyDate && row.modifyDate.toISOString(), + createSql: row.definition, + triggerTiming: row.triggerTiming, + eventType: row.eventType, + schemaName: row.schemaName, + tableName: row.tableName, + triggerName: row.triggerName, + })); + this.feedback({ analysingMessage: null }); return { tables, views, procedures, functions, + triggers, }; } diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/index.js b/plugins/dbgate-plugin-mssql/src/backend/sql/index.js index 6352b6a9b..4125b1ebd 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/index.js @@ -12,6 +12,7 @@ const functionParameters = require('./functionParameters'); const viewColumns = require('./viewColumns'); const indexes = require('./indexes'); const indexcols = require('./indexcols'); +const triggers = require('./triggers'); module.exports = { columns, @@ -28,4 +29,5 @@ module.exports = { indexes, indexcols, tableSizes, + triggers, }; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js b/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js new file mode 100644 index 000000000..e37a9ef06 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js @@ -0,0 +1,28 @@ +module.exports = ` +SELECT + o.modify_date as modifyDate, + o.object_id as objectId, + o.name AS triggerName, + s.name AS schemaName, + OBJECT_NAME(o.parent_object_id) AS tableName, + CASE + WHEN OBJECTPROPERTY(o.object_id, 'ExecIsAfterTrigger') = 1 THEN 'AFTER' + WHEN OBJECTPROPERTY(o.object_id, 'ExecIsInsteadOfTrigger') = 1 THEN 'INSTEAD OF' + ELSE 'BEFORE' + END AS triggerTiming, + CASE + WHEN OBJECTPROPERTY(o.object_id, 'ExecIsInsertTrigger') = 1 THEN 'INSERT' + WHEN OBJECTPROPERTY(o.object_id, 'ExecIsUpdateTrigger') = 1 THEN 'UPDATE' + WHEN OBJECTPROPERTY(o.object_id, 'ExecIsDeleteTrigger') = 1 THEN 'DELETE' + END AS triggerEvent, + OBJECT_DEFINITION(o.object_id) AS definition +FROM sys.objects o +INNER JOIN sys.tables t + ON o.parent_object_id = t.object_id +INNER JOIN sys.schemas s + ON t.schema_id = s.schema_id +WHERE o.type = 'TR' + AND o.is_ms_shipped = 0 + AND o.object_id =OBJECT_ID_CONDITION + AND s.name =SCHEMA_NAME_CONDITION +`; From c00cb3076c2f81105ccb83ea9ece0184bf3b4437 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 12 Dec 2024 20:12:17 +0100 Subject: [PATCH 03/23] feat: add mysql triggers to analyser --- plugins/dbgate-plugin-mysql/src/backend/Analyser.js | 13 +++++++++++++ .../dbgate-plugin-mysql/src/backend/sql/index.js | 2 ++ .../dbgate-plugin-mysql/src/backend/sql/triggers.js | 13 +++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index 1ff963894..30363106b 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -160,6 +160,9 @@ class Analyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Loading indexes' }); const indexes = await this.analyserQuery('indexes', ['tables']); this.feedback({ analysingMessage: 'Loading uniques' }); + + const triggers = await this.analyserQuery('triggers'); + const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); this.feedback({ analysingMessage: 'Finalizing DB structure' }); @@ -236,6 +239,16 @@ class Analyser extends DatabaseAnalyser { contentHash: _.isDate(x.modifyDate) ? x.modifyDate.toISOString() : x.modifyDate, parameters: functionNameToParameters[x.pureName], })), + triggers: triggers.rows.map(row => ({ + objectId: row.triggerName, + contentHash: row.modifyDate, + triggerName: row.triggerName, + eventType: row.triggerEvent, + triggerTiming: row.triggerTiming, + schemaName: row.schemaName, + tableName: row.tableName, + createSql: row.definition, + })), }; this.feedback({ analysingMessage: null }); return res; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/index.js b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js index 8c3e095fa..1a7eda8d7 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js @@ -11,6 +11,7 @@ const functionModifications = require('./functionModifications'); const uniqueNames = require('./uniqueNames'); const viewTexts = require('./viewTexts'); const parameters = require('./parameters'); +const triggers = require('./triggers'); module.exports = { columns, @@ -26,4 +27,5 @@ module.exports = { indexes, uniqueNames, viewTexts, + triggers, }; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js b/plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js new file mode 100644 index 000000000..f548d96e7 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js @@ -0,0 +1,13 @@ +module.exports = ` +SELECT + TRIGGER_NAME AS triggerName, + EVENT_MANIPULATION AS eventType, + ACTION_TIMING AS triggerTiming, + EVENT_OBJECT_SCHEMA AS schemaName, + EVENT_OBJECT_TABLE AS tableName, + ACTION_STATEMENT AS definition, + CREATED as modifyDate +FROM + INFORMATION_SCHEMA.TRIGGERS + WHERE EVENT_OBJECT_SCHEMA = '#DATABASE#' AND TRIGGER_NAME =OBJECT_ID_CONDITION +`; From 671eba22e02f6fb4ca35bee144d300c7bed180f0 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Fri, 13 Dec 2024 04:09:32 +0100 Subject: [PATCH 04/23] feat: add oracle triggers to analyser --- .../src/backend/Analyser.js | 10 ++++++++++ .../src/backend/sql/index.js | 4 +++- .../src/backend/sql/triggers.js | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js diff --git a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js index 397f7aa5f..c2617bc4f 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js @@ -71,6 +71,9 @@ class Analyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Loading indexes' }); const indexes = await this.analyserQuery('indexes', ['tables'], { $owner: this.dbhan.database }); this.feedback({ analysingMessage: 'Loading unique names' }); + + const triggers = await this.analyserQuery('triggers', undefined, { $owner: this.dbhan.database }); + const uniqueNames = await this.analyserQuery('uniqueNames', ['tables'], { $owner: this.dbhan.database }); this.feedback({ analysingMessage: 'Finalizing DB structure' }); @@ -183,6 +186,13 @@ class Analyser extends DatabaseAnalyser { // schemaName: func.schema_name, contentHash: func.hash_code, })), + triggers: triggers.rows.map(row => ({ + trigerName: row.TRIGGER_NAME, + definition: row.DEFINITION, + tableName: row.TABLE_NAME, + triggerLevel: row.TRIGGER_LEVEL, + eventType: row.EVENT_TYPE, + })), }; this.feedback({ analysingMessage: null }); diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/index.js b/plugins/dbgate-plugin-oracle/src/backend/sql/index.js index ca1b3f24f..70e7bf59c 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/index.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/index.js @@ -6,6 +6,7 @@ const views = require('./views'); const matviews = require('./matviews'); const routines = require('./routines'); const indexes = require('./indexes'); // use mysql +const triggers = require('./triggers'); //const indexcols = require('./indexcols'); const uniqueNames = require('./uniqueNames'); //const geometryColumns = require('./geometryColumns'); @@ -24,7 +25,8 @@ module.exports = { routines, matviews, indexes, -// indexcols, + triggers, + // indexcols, uniqueNames, //geometryColumns, //geographyColumns, diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js b/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js new file mode 100644 index 000000000..69ebda3d5 --- /dev/null +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js @@ -0,0 +1,18 @@ +module.exports = ` +SELECT + TRIGGER_TYPE AS event_type, + TRIGGER_BODY AS definition, + TRIGGER_NAME AS trigger_name, + TABLE_NAME AS table_name, + OWNER, + CASE + WHEN INSTR(TRIGGER_TYPE, 'ROW') > 0 THEN 'ROW' + WHEN INSTR(TRIGGER_TYPE, 'STATEMENT') > 0 THEN 'STATEMENT' + ELSE NULL + END AS trigger_level +FROM + all_triggers +WHERE + OWNER='$owner' + AND 'tables:' || TABLE_NAME =OBJECT_ID_CONDITION +`; From 1c84f40bcf0a49f75e965a5b1180dc80b135f945 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Fri, 13 Dec 2024 04:13:39 +0100 Subject: [PATCH 05/23] feat: add triggers loading message --- plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js | 5 +++-- plugins/dbgate-plugin-mysql/src/backend/Analyser.js | 1 + plugins/dbgate-plugin-oracle/src/backend/Analyser.js | 1 + plugins/dbgate-plugin-postgres/src/backend/Analyser.js | 5 +++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js index 84b8168c3..4d51a4339 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -132,6 +132,9 @@ class MsSqlAnalyser extends DatabaseAnalyser { const procedureParameterRows = await this.analyserQuery('proceduresParameters'); const functionParameterRows = await this.analyserQuery('functionParameters'); + this.feedback({ analysingMessage: 'Loading triggers' }); + const triggerRows = await this.analyserQuery('triggers'); + this.feedback({ analysingMessage: 'Loading view columns' }); const viewColumnRows = await this.analyserQuery('viewColumns', ['views']); @@ -214,8 +217,6 @@ class MsSqlAnalyser extends DatabaseAnalyser { parameters: functionToParameters[row.objectId], })); - const triggerRows = await this.analyserQuery('triggers'); - const triggers = triggerRows.rows.map(row => ({ objectId: `triggers:${row.objectId}`, contentHash: row.modifyDate && row.modifyDate.toISOString(), diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index 30363106b..d85b7c3c6 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -161,6 +161,7 @@ class Analyser extends DatabaseAnalyser { const indexes = await this.analyserQuery('indexes', ['tables']); this.feedback({ analysingMessage: 'Loading uniques' }); + this.feedback({ analysingMessage: 'Loading triggers' }); const triggers = await this.analyserQuery('triggers'); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); diff --git a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js index c2617bc4f..d4f204e71 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js @@ -73,6 +73,7 @@ class Analyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Loading unique names' }); const triggers = await this.analyserQuery('triggers', undefined, { $owner: this.dbhan.database }); + this.feedback({ analysingMessage: 'Loading triggers' }); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables'], { $owner: this.dbhan.database }); this.feedback({ analysingMessage: 'Finalizing DB structure' }); diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js index 428fef847..784fde489 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js @@ -185,6 +185,9 @@ class Analyser extends DatabaseAnalyser { geographyColumns = await this.analyserQuery('geographyColumns', ['tables']); } + this.feedback({ analysingMessage: 'Loading triggers' }); + const triggers = await this.analyserQuery('triggers'); + this.feedback({ analysingMessage: 'Finalizing DB structure' }); const columnColumnsMapped = fkColumns.rows.map(x => ({ @@ -241,8 +244,6 @@ class Analyser extends DatabaseAnalyser { return acc; }, {}); - const triggers = await this.analyserQuery('triggers'); - const res = { tables: tables.rows.map(table => { const newTable = { From d32af771dce346cb25ef0c5819b39c57eb790986 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Tue, 17 Dec 2024 09:09:37 +0100 Subject: [PATCH 06/23] fix: add purename to trigger analysers --- .../dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js | 1 + plugins/dbgate-plugin-mysql/src/backend/Analyser.js | 1 + plugins/dbgate-plugin-oracle/src/backend/Analyser.js | 1 + .../dbgate-plugin-postgres/src/backend/Analyser.js | 11 +++++++++-- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js index 4d51a4339..385eef35e 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -226,6 +226,7 @@ class MsSqlAnalyser extends DatabaseAnalyser { schemaName: row.schemaName, tableName: row.tableName, triggerName: row.triggerName, + pureName: row.triggerName, })); this.feedback({ analysingMessage: null }); diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index d85b7c3c6..f93ea2eed 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -244,6 +244,7 @@ class Analyser extends DatabaseAnalyser { objectId: row.triggerName, contentHash: row.modifyDate, triggerName: row.triggerName, + pureName: row.triggerName, eventType: row.triggerEvent, triggerTiming: row.triggerTiming, schemaName: row.schemaName, diff --git a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js index d4f204e71..64b016671 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js @@ -188,6 +188,7 @@ class Analyser extends DatabaseAnalyser { contentHash: func.hash_code, })), triggers: triggers.rows.map(row => ({ + pureName: row.TRIGGER_NAME, trigerName: row.TRIGGER_NAME, definition: row.DEFINITION, tableName: row.TABLE_NAME, diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js index 784fde489..f545e2761 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js @@ -114,13 +114,19 @@ class Analyser extends DatabaseAnalyser { // if (!cntBase || !cntRef) continue; const baseCols = _.sortBy( fk_keyColumnUsage.rows.filter( - x => x.table_name == fkRef.table_name && x.constraint_name == fkRef.constraint_name && x.table_schema == fkRef.table_schema + x => + x.table_name == fkRef.table_name && + x.constraint_name == fkRef.constraint_name && + x.table_schema == fkRef.table_schema ), 'ordinal_position' ); const refCols = _.sortBy( fk_keyColumnUsage.rows.filter( - x => x.table_name == fkRef.ref_table_name && x.constraint_name == fkRef.unique_constraint_name && x.table_schema == fkRef.ref_table_schema + x => + x.table_name == fkRef.ref_table_name && + x.constraint_name == fkRef.unique_constraint_name && + x.table_schema == fkRef.ref_table_schema ), 'ordinal_position' ); @@ -352,6 +358,7 @@ class Analyser extends DatabaseAnalyser { returnType: func.data_type, })), triggers: triggers.rows.map(row => ({ + pureName: row.trigger_name, trigerName: row.trigger_name, functionName: row.function_name, triggerTiming: row.trigger_timing, From 9e941dfce21d66ebac73ad28973dfef7ac0d8f73 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Tue, 17 Dec 2024 10:08:23 +0100 Subject: [PATCH 07/23] feat: add triggers to ui --- .../src/appobj/DatabaseObjectAppObject.svelte | 44 ++++--------------- packages/web/src/appobj/appObjectTools.ts | 8 ++++ packages/web/src/icons/FontIcon.svelte | 1 + .../web/src/utility/applyScriptTemplate.ts | 7 +++ packages/web/src/widgets/SqlObjectList.svelte | 2 +- 5 files changed, 26 insertions(+), 36 deletions(-) diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index de1fd3f4e..5a43f2f5f 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -39,6 +39,7 @@ procedures: 'img procedure', functions: 'img function', queries: 'img query-data', + triggers: 'icon trigger', }; const defaultTabs = { @@ -49,6 +50,7 @@ queries: 'QueryDataTab', procedures: 'SqlObjectTab', functions: 'SqlObjectTab', + triggers: 'SqlObjectTab', }; function createScriptTemplatesSubmenu(objectTypeField) { @@ -340,40 +342,8 @@ }, ]; case 'functions': - return [ - ...defaultDatabaseObjectAppObjectActions['functions'], - { - divider: true, - }, - hasPermission('dbops/model/edit') && { - label: 'Drop function', - isDrop: true, - requiresWriteAccess: true, - }, - hasPermission('dbops/model/edit') && { - label: 'Rename function', - isRename: true, - requiresWriteAccess: true, - }, - createScriptTemplatesSubmenu('functions'), - { - label: 'SQL generator', - submenu: [ - { - label: 'CREATE FUNCTION', - sqlGeneratorProps: { - createFunctions: true, - }, - }, - { - label: 'DROP FUNCTION', - sqlGeneratorProps: { - dropFunctions: true, - }, - }, - ], - }, - ]; + case 'triggers': + return [...defaultDatabaseObjectAppObjectActions['triggers']]; case 'collections': return [ ...defaultDatabaseObjectAppObjectActions['collections'], @@ -751,7 +721,7 @@ return; } - const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField]; + const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField] ?? []; const configuredActionId = getLastUsedDefaultActions()[objectTypeField]; const prefferedAction = @@ -953,6 +923,10 @@ function getExtInfo(data) { const res = []; + if (data.objectTypeField === 'triggers') { + res.push(`${data.triggerTiming ?? ''} ${data.eventType ?? ''}`.toLowerCase()); + } + if (data.objectComment) { res.push(data.objectComment); } diff --git a/packages/web/src/appobj/appObjectTools.ts b/packages/web/src/appobj/appObjectTools.ts index 1eb48e45f..7b3e0bb8c 100644 --- a/packages/web/src/appobj/appObjectTools.ts +++ b/packages/web/src/appobj/appObjectTools.ts @@ -60,6 +60,14 @@ export const defaultDatabaseObjectAppObjectActions = { icon: 'img sql-file', }, ], + triggers: [ + { + label: 'Show SQL', + tab: 'SqlObjectTab', + defaultActionId: 'showSql', + icon: 'img sql-file', + }, + ], collections: [ { label: 'Open data', diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index 4cf0e103a..0f4a63012 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -64,6 +64,7 @@ 'icon add-folder': 'mdi mdi-folder-plus-outline', 'icon add-column': 'mdi mdi-table-column-plus-after', 'icon parameter': 'mdi mdi-at', + 'icon trigger': 'mdi mdi-lightning-bolt', 'icon window-restore': 'mdi mdi-window-restore', 'icon window-maximize': 'mdi mdi-window-maximize', diff --git a/packages/web/src/utility/applyScriptTemplate.ts b/packages/web/src/utility/applyScriptTemplate.ts index 124e32f21..ac2de4b0f 100644 --- a/packages/web/src/utility/applyScriptTemplate.ts +++ b/packages/web/src/utility/applyScriptTemplate.ts @@ -166,6 +166,13 @@ export function getSupportedScriptTemplates(objectTypeField: string): { label: s scriptTemplate: 'CALL OBJECT', }, ]; + case 'triggers': + return [ + { + label: 'CREATE TRIGGER', + scriptTemplate: 'CREATE OBJECT', + }, + ]; } return []; diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 7403ffe56..eb43fd116 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -80,7 +80,7 @@ // $: console.log('OBJECTS', $objects); $: objectList = _.flatten([ - ...['tables', 'collections', 'views', 'matviews', 'procedures', 'functions'].map(objectTypeField => + ...['tables', 'collections', 'views', 'matviews', 'procedures', 'functions', 'triggers'].map(objectTypeField => _.sortBy( (($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })), ['schemaName', 'pureName'] From 413287c691ea4feb14d7dfac7a576ea4a08847ac Mon Sep 17 00:00:00 2001 From: Nybkox Date: Tue, 17 Dec 2024 10:15:57 +0100 Subject: [PATCH 08/23] fix: remove triggerName --- packages/types/dbinfo.d.ts | 1 - plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js | 1 - plugins/dbgate-plugin-mysql/src/backend/Analyser.js | 2 -- 3 files changed, 4 deletions(-) diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 0cf0794b4..58edc082a 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -138,7 +138,6 @@ export interface FunctionInfo extends CallableObjectInfo { export interface TriggerInfo extends SqlObjectInfo { objectId: string; - trigerName: string; functionName?: string; tableName: string; triggerTiming?: 'BEFORE' | 'AFTER' | 'INSTEAD OF' | null; diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js index 385eef35e..1451c0177 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -225,7 +225,6 @@ class MsSqlAnalyser extends DatabaseAnalyser { eventType: row.eventType, schemaName: row.schemaName, tableName: row.tableName, - triggerName: row.triggerName, pureName: row.triggerName, })); diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index f93ea2eed..2bafc996e 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -241,9 +241,7 @@ class Analyser extends DatabaseAnalyser { parameters: functionNameToParameters[x.pureName], })), triggers: triggers.rows.map(row => ({ - objectId: row.triggerName, contentHash: row.modifyDate, - triggerName: row.triggerName, pureName: row.triggerName, eventType: row.triggerEvent, triggerTiming: row.triggerTiming, From 5f4bd6d3e3da4e92ceca6754b99a6d2f536f1ddc Mon Sep 17 00:00:00 2001 From: Nybkox Date: Wed, 18 Dec 2024 06:05:49 +0100 Subject: [PATCH 09/23] fix: triggers typing and yaml conv --- packages/tools/src/yamlModelConv.ts | 1 + packages/types/dbinfo.d.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/tools/src/yamlModelConv.ts b/packages/tools/src/yamlModelConv.ts index da4fe5219..4ed6e3b9b 100644 --- a/packages/tools/src/yamlModelConv.ts +++ b/packages/tools/src/yamlModelConv.ts @@ -234,6 +234,7 @@ export function databaseInfoFromYamlModel(filesOrDbInfo: DatabaseModelFile[] | D if (file.name.endsWith('.trigger.sql')) { model.triggers.push({ + objectId: `triggers:${file.name.slice(0, -'.trigger.sql'.length)}`, pureName: file.name.slice(0, -'.trigger.sql'.length), createSql: file.text, }); diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 58edc082a..06eecd8a1 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -139,7 +139,7 @@ export interface FunctionInfo extends CallableObjectInfo { export interface TriggerInfo extends SqlObjectInfo { objectId: string; functionName?: string; - tableName: string; + tableName?: string; triggerTiming?: 'BEFORE' | 'AFTER' | 'INSTEAD OF' | null; triggerLevel?: 'ROW' | 'STATEMENT'; eventType?: 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE'; From e866c019f0f45da036a19bc790d6506383759203 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Wed, 18 Dec 2024 06:09:30 +0100 Subject: [PATCH 10/23] feat: mysql and oracle triggers tests --- .../__tests__/object-analyse.spec.js | 20 ++++++ integration-tests/engines.js | 68 ++++++++++++++++--- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/integration-tests/__tests__/object-analyse.spec.js b/integration-tests/__tests__/object-analyse.spec.js index f924f2137..f9fce1dfc 100644 --- a/integration-tests/__tests__/object-analyse.spec.js +++ b/integration-tests/__tests__/object-analyse.spec.js @@ -19,6 +19,10 @@ function flatSourceParameters() { ); } +function flatSourceTriggers() { + return _.flatten(engines.map(engine => (engine.triggers || []).map(trigger => [engine.label, trigger, engine]))); +} + const obj1Match = expect.objectContaining({ pureName: 'obj1', }); @@ -136,4 +140,20 @@ describe('Object analyse', () => { } }) ); + + test.each(flatSourceTriggers())( + 'Test triggers - %s - %s', + testWrapper(async (conn, driver, testName, trigger, engine) => { + for (const sql of initSql) await runCommandOnDriver(conn, driver, sql); + + await runCommandOnDriver(conn, driver, trigger.create); + const structure = await driver.analyseFull(conn); + await runCommandOnDriver(conn, driver, trigger.drop); + + const createdTrigger = structure[trigger.objectTypeField].find(x => x.pureName == trigger.pureName); + expect(createdTrigger).toEqual(expect.objectContaining(trigger.expected)); + }) + ); }); + +console.log(flatSourceTriggers()); diff --git a/integration-tests/engines.js b/integration-tests/engines.js index da452af83..485698d04 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -108,6 +108,30 @@ const engines = [ ], }, ], + triggers: [ + { + testName: 'triggers insert after', + create: 'CREATE TRIGGER obj1 AFTER INSERT ON t1 FOR EACH ROW BEGIN END', + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'INSERT', + triggerTiming: 'AFTER', + }, + }, + { + testName: 'triggers insert before', + create: 'CREATE TRIGGER obj1 BEFORE INSERT ON t1 FOR EACH ROW BEGIN END', + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'INSERT', + triggerTiming: 'BEFORE', + }, + }, + ], }, { label: 'MariaDB', @@ -429,25 +453,49 @@ end;$$`, }, { type: 'functions', - create1: 'CREATE FUNCTION ~obj1 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t1;\n RETURN v_count;\n END ~obj1', - create2: 'CREATE FUNCTION ~obj2 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t2;\n RETURN v_count;\n END ~obj2', + create1: + 'CREATE FUNCTION ~obj1 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t1;\n RETURN v_count;\n END ~obj1', + create2: + 'CREATE FUNCTION ~obj2 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t2;\n RETURN v_count;\n END ~obj2', drop1: 'DROP FUNCTION ~obj1', drop2: 'DROP FUNCTION ~obj2', }, ], + triggers: [ + { + testName: 'triggers after each row', + create: 'CREATE OR REPLACE TRIGGER obj1 AFTER INSERT ON T1 FOR EACH ROW BEGIN END obj1;', + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'AFTER EACH ROW', + }, + }, + { + testName: 'triggers before each row', + create: 'CREATE OR REPLACE TRIGGER obj1 BEFORE INSERT ON T1 FOR EACH ROW BEGIN END obj1;', + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'BEFORE EACH ROW', + }, + }, + ], }, ]; const filterLocal = [ // filter local testing - '-MySQL', - '-MariaDB', - '-PostgreSQL', - '-SQL Server', - '-SQLite', - '-CockroachDB', - '-ClickHouse', - 'Oracle', + 'MySQL', + // '-MariaDB', + // '-PostgreSQL', + // '-SQL Server', + // '-SQLite', + // '-CockroachDB', + // '-ClickHouse', + // 'Oracle', ]; const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL'); From d2c5440e39c24618d5c8288d39329dab0c75ae4c Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 08:33:26 +0100 Subject: [PATCH 11/23] feat: pssql tirgger test --- integration-tests/engines.js | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/integration-tests/engines.js b/integration-tests/engines.js index 485698d04..dbe4c2e6e 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -288,6 +288,50 @@ end;$$`, ], }, ], + triggers: [ + { + testName: 'triggers after each row', + create: `CREATE TRIGGER obj1 +BEFORE INSERT ON t1 +FOR EACH ROW +EXECUTE FUNCTION test_function(); +`, + drop: 'DROP TRIGGER obj1;', + triggerOtherCreateSql: `CREATE OR REPLACE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN +END; +$$ LANGUAGE plpgsql;`, + triggerOtherDropSql: 'DROP FUNCTION test_function', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'insert', + triggerTiming: 'after', + }, + }, + { + testName: 'triggers before each row', + create: `CREATE TRIGGER obj1 +BEFORE INSERT ON t1 +FOR EACH ROW +EXECUTE FUNCTION test_function(); +`, + drop: 'DROP TRIGGER obj1;', + triggerOtherCreateSql: `CREATE OR REPLACE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN +END; +$$ LANGUAGE plpgsql;`, + triggerOtherDropSql: 'DROP FUNCTION test_function', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'insert', + triggerTiming: 'before', + }, + }, + ], }, { label: 'SQL Server', From d75b9e26885bb5a6c7e950cc900cc7f120c2f5e5 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Thu, 19 Dec 2024 09:21:57 +0100 Subject: [PATCH 12/23] ace-builds patch #954 --- patches/ace-builds+1.33.1.patch | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 patches/ace-builds+1.33.1.patch diff --git a/patches/ace-builds+1.33.1.patch b/patches/ace-builds+1.33.1.patch new file mode 100644 index 000000000..333bc1aaf --- /dev/null +++ b/patches/ace-builds+1.33.1.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/ace-builds/src-noconflict/mode-sqlserver.js b/node_modules/ace-builds/src-noconflict/mode-sqlserver.js +index 7e61d3d..3e0fb97 100644 +--- a/node_modules/ace-builds/src-noconflict/mode-sqlserver.js ++++ b/node_modules/ace-builds/src-noconflict/mode-sqlserver.js +@@ -304,7 +304,7 @@ var BaseFoldMode = require("./cstyle").FoldMode; + var FoldMode = exports.FoldMode = function () { }; + oop.inherits(FoldMode, BaseFoldMode); + (function () { +- this.foldingStartMarker = /(\bCASE\b|\bBEGIN\b)|^\s*(\/\*)/i; ++ this.foldingStartMarker = /(? Date: Thu, 19 Dec 2024 09:53:11 +0100 Subject: [PATCH 13/23] fix: pssql triggers - map tgtype to timing / event type correctly --- .../src/backend/sql/triggers.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js index 6b8cdd781..7d9f82cb3 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js @@ -10,18 +10,17 @@ SELECT WHEN t.tgtype & 1 = 1 THEN 'ROW' ELSE 'STATEMENT' END AS trigger_level, - CASE - WHEN tgtype & 2 = 2 THEN 'AFTER' - WHEN tgtype & 1 = 1 THEN 'BEFORE' - ELSE NULL - END AS trigger_timing, - CASE - WHEN tgtype & 32 = 32 THEN 'TRUNCATE' - WHEN tgtype & 16 = 16 THEN 'UPDATE' - WHEN tgtype & 8 = 8 THEN 'DELETE' - WHEN tgtype & 4 = 4 THEN 'INSERT' - ELSE NULL - END AS event_type, + COALESCE( + CASE WHEN (tgtype::int::bit(7) & b'0000010')::int = 0 THEN NULL ELSE 'BEFORE' END, + CASE WHEN (tgtype::int::bit(7) & b'0000010')::int = 0 THEN 'AFTER' ELSE NULL END, + CASE WHEN (tgtype::int::bit(7) & b'1000000')::int = 0 THEN NULL ELSE 'INSTEAD' END, + '' + )::text as trigger_timing, + (CASE WHEN (tgtype::int::bit(7) & b'0000100')::int = 0 THEN '' ELSE 'INSERT' END) || + (CASE WHEN (tgtype::int::bit(7) & b'0001000')::int = 0 THEN '' ELSE 'DELETE' END) || + (CASE WHEN (tgtype::int::bit(7) & b'0010000')::int = 0 THEN '' ELSE 'UPDATE' END) || + (CASE WHEN (tgtype::int::bit(7) & b'0100000')::int = 0 THEN '' ELSE 'TRUNCATE' END) + as event_type, pg_get_triggerdef(t.oid) AS definition FROM pg_trigger t From 022f263bf97e5616f9537ca5164178e98a9313aa Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 09:53:47 +0100 Subject: [PATCH 14/23] fix: update pssql triggers test sql --- integration-tests/engines.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/integration-tests/engines.js b/integration-tests/engines.js index dbe4c2e6e..da3e47f17 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -292,11 +292,11 @@ end;$$`, { testName: 'triggers after each row', create: `CREATE TRIGGER obj1 -BEFORE INSERT ON t1 +AFTER INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION test_function(); `, - drop: 'DROP TRIGGER obj1;', + drop: 'DROP TRIGGER obj1 ON t1;', triggerOtherCreateSql: `CREATE OR REPLACE FUNCTION test_function() RETURNS TRIGGER AS $$ BEGIN @@ -306,8 +306,8 @@ $$ LANGUAGE plpgsql;`, objectTypeField: 'triggers', expected: { pureName: 'obj1', - eventType: 'insert', - triggerTiming: 'after', + eventType: 'INSERT', + triggerTiming: 'AFTER', }, }, { @@ -317,7 +317,7 @@ BEFORE INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION test_function(); `, - drop: 'DROP TRIGGER obj1;', + drop: 'DROP TRIGGER obj1 ON t1;', triggerOtherCreateSql: `CREATE OR REPLACE FUNCTION test_function() RETURNS TRIGGER AS $$ BEGIN @@ -327,8 +327,8 @@ $$ LANGUAGE plpgsql;`, objectTypeField: 'triggers', expected: { pureName: 'obj1', - eventType: 'insert', - triggerTiming: 'before', + eventType: 'INSERT', + triggerTiming: 'BEFORE', }, }, ], @@ -532,9 +532,9 @@ $$ LANGUAGE plpgsql;`, const filterLocal = [ // filter local testing - 'MySQL', + // 'MySQL', // '-MariaDB', - // '-PostgreSQL', + 'PostgreSQL', // '-SQL Server', // '-SQLite', // '-CockroachDB', From 9eb3bca8d6d5005263117367a14468951c23f9ab Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 09:54:17 +0100 Subject: [PATCH 15/23] feat: add other create/drop for triggers tests --- .../__tests__/object-analyse.spec.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/integration-tests/__tests__/object-analyse.spec.js b/integration-tests/__tests__/object-analyse.spec.js index f9fce1dfc..8a8956dd5 100644 --- a/integration-tests/__tests__/object-analyse.spec.js +++ b/integration-tests/__tests__/object-analyse.spec.js @@ -143,15 +143,21 @@ describe('Object analyse', () => { test.each(flatSourceTriggers())( 'Test triggers - %s - %s', - testWrapper(async (conn, driver, testName, trigger, engine) => { + testWrapper(async (conn, driver, trigger) => { for (const sql of initSql) await runCommandOnDriver(conn, driver, sql); - await runCommandOnDriver(conn, driver, trigger.create); - const structure = await driver.analyseFull(conn); - await runCommandOnDriver(conn, driver, trigger.drop); + const { triggerOtherDropSql, triggerOtherCreateSql, create, drop, expected, objectTypeField } = trigger; - const createdTrigger = structure[trigger.objectTypeField].find(x => x.pureName == trigger.pureName); - expect(createdTrigger).toEqual(expect.objectContaining(trigger.expected)); + if (triggerOtherCreateSql) await runCommandOnDriver(conn, driver, triggerOtherCreateSql); + + await runCommandOnDriver(conn, driver, create); + const structure = await driver.analyseFull(conn); + await runCommandOnDriver(conn, driver, drop); + + if (triggerOtherDropSql) await runCommandOnDriver(conn, driver, triggerOtherDropSql); + + const createdTrigger = structure[objectTypeField].find(x => x.pureName == expected.pureName); + expect(createdTrigger).toEqual(expect.objectContaining(expected)); }) ); }); From b9939e5d5f0d0dffb26bcc7185d72378b8ac728a Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 09:58:54 +0100 Subject: [PATCH 16/23] fix: correctly map mysql triggers sql output --- plugins/dbgate-plugin-mysql/src/backend/Analyser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js index 2bafc996e..34b83a0ba 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -243,7 +243,7 @@ class Analyser extends DatabaseAnalyser { triggers: triggers.rows.map(row => ({ contentHash: row.modifyDate, pureName: row.triggerName, - eventType: row.triggerEvent, + eventType: row.eventType, triggerTiming: row.triggerTiming, schemaName: row.schemaName, tableName: row.tableName, From ebb4f8e73ea6bffe1f11c55c89abeb1fda5f7833 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:13:04 +0100 Subject: [PATCH 17/23] fix: mssql triggers query naming --- plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js b/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js index e37a9ef06..3cf1dd1e8 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js @@ -14,7 +14,7 @@ SELECT WHEN OBJECTPROPERTY(o.object_id, 'ExecIsInsertTrigger') = 1 THEN 'INSERT' WHEN OBJECTPROPERTY(o.object_id, 'ExecIsUpdateTrigger') = 1 THEN 'UPDATE' WHEN OBJECTPROPERTY(o.object_id, 'ExecIsDeleteTrigger') = 1 THEN 'DELETE' - END AS triggerEvent, + END AS eventType, OBJECT_DEFINITION(o.object_id) AS definition FROM sys.objects o INNER JOIN sys.tables t From e08216d6dd3caa8fb39e5aa2095d3b1857457e52 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:13:15 +0100 Subject: [PATCH 18/23] feat: mssql triggers tests --- integration-tests/engines.js | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/integration-tests/engines.js b/integration-tests/engines.js index da3e47f17..6cc4abdad 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -417,6 +417,42 @@ $$ LANGUAGE plpgsql;`, supportRenameSqlObject: true, defaultSchemaName: 'dbo', // skipSeparateSchemas: true, + triggers: [ + { + testName: 'triggers before each row', + create: `CREATE TRIGGER obj1 +ON t1 +AFTER INSERT +AS +BEGIN +SELECT * FROM t1 +END;`, + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'INSERT', + triggerTiming: 'AFTER', + }, + }, + { + testName: 'triggers before each row', + create: `CREATE TRIGGER obj1 +ON t1 +AFTER UPDATE +AS +BEGIN +SELECT * FROM t1 +END;`, + drop: 'DROP TRIGGER obj1;', + objectTypeField: 'triggers', + expected: { + pureName: 'obj1', + eventType: 'UPDATE', + triggerTiming: 'AFTER', + }, + }, + ], }, { label: 'SQLite', @@ -532,10 +568,10 @@ $$ LANGUAGE plpgsql;`, const filterLocal = [ // filter local testing - // 'MySQL', + '-MySQL', // '-MariaDB', - 'PostgreSQL', - // '-SQL Server', + '-PostgreSQL', + 'SQL Server', // '-SQLite', // '-CockroachDB', // '-ClickHouse', From f16aab12e176317c4e1183565a2991db539c565a Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:17:16 +0100 Subject: [PATCH 19/23] fix: add trigger event type to oracle triggers query --- plugins/dbgate-plugin-oracle/src/backend/Analyser.js | 1 + plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js index 64b016671..b25676134 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js @@ -193,6 +193,7 @@ class Analyser extends DatabaseAnalyser { definition: row.DEFINITION, tableName: row.TABLE_NAME, triggerLevel: row.TRIGGER_LEVEL, + triggerTiming: row.TRIGGER_TIMING, eventType: row.EVENT_TYPE, })), }; diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js b/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js index 69ebda3d5..78517df06 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js @@ -1,6 +1,7 @@ module.exports = ` SELECT - TRIGGER_TYPE AS event_type, + TRIGGER_TYPE AS trigger_timing, + TRIGGERING_EVENT AS event_type, TRIGGER_BODY AS definition, TRIGGER_NAME AS trigger_name, TABLE_NAME AS table_name, From 394020157b91ba12a567fc858acd39570008f2dc Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:26:28 +0100 Subject: [PATCH 20/23] fix: update oracle triggers tests expected values --- integration-tests/engines.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/integration-tests/engines.js b/integration-tests/engines.js index 6cc4abdad..361b8ca0b 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -548,8 +548,9 @@ END;`, drop: 'DROP TRIGGER obj1;', objectTypeField: 'triggers', expected: { - pureName: 'obj1', - eventType: 'AFTER EACH ROW', + pureName: 'OBJ1', + eventType: 'INSERT', + triggerTiming: 'AFTER EACH ROW', }, }, { @@ -558,8 +559,9 @@ END;`, drop: 'DROP TRIGGER obj1;', objectTypeField: 'triggers', expected: { - pureName: 'obj1', - eventType: 'BEFORE EACH ROW', + pureName: 'OBJ1', + eventType: 'INSERT', + triggerTiming: 'BEFORE EACH ROW', }, }, ], From af4e91faba46a648b9111112b69bd7f69f526336 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:46:40 +0100 Subject: [PATCH 21/23] fix: update oracle triggers test create queries --- integration-tests/engines.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/engines.js b/integration-tests/engines.js index 361b8ca0b..a9bd1bc9b 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -544,7 +544,7 @@ END;`, triggers: [ { testName: 'triggers after each row', - create: 'CREATE OR REPLACE TRIGGER obj1 AFTER INSERT ON T1 FOR EACH ROW BEGIN END obj1;', + create: 'CREATE OR REPLACE TRIGGER obj1 AFTER INSERT ON "t1" FOR EACH ROW BEGIN END obj1;', drop: 'DROP TRIGGER obj1;', objectTypeField: 'triggers', expected: { @@ -555,7 +555,7 @@ END;`, }, { testName: 'triggers before each row', - create: 'CREATE OR REPLACE TRIGGER obj1 BEFORE INSERT ON T1 FOR EACH ROW BEGIN END obj1;', + create: 'CREATE OR REPLACE TRIGGER obj1 BEFORE INSERT ON "t1" FOR EACH ROW BEGIN END obj1;', drop: 'DROP TRIGGER obj1;', objectTypeField: 'triggers', expected: { From dc9bfdc5538a8cbea56a799472be61f6e217c338 Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 10:50:39 +0100 Subject: [PATCH 22/23] fix: update pssql triggers query to match ts def --- plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js index 7d9f82cb3..f5afcd4b1 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js @@ -13,7 +13,7 @@ SELECT COALESCE( CASE WHEN (tgtype::int::bit(7) & b'0000010')::int = 0 THEN NULL ELSE 'BEFORE' END, CASE WHEN (tgtype::int::bit(7) & b'0000010')::int = 0 THEN 'AFTER' ELSE NULL END, - CASE WHEN (tgtype::int::bit(7) & b'1000000')::int = 0 THEN NULL ELSE 'INSTEAD' END, + CASE WHEN (tgtype::int::bit(7) & b'1000000')::int = 0 THEN NULL ELSE 'INSTEAD OF' END, '' )::text as trigger_timing, (CASE WHEN (tgtype::int::bit(7) & b'0000100')::int = 0 THEN '' ELSE 'INSERT' END) || From 9ccd647d97683b023c7ba80472c788d60505864d Mon Sep 17 00:00:00 2001 From: Nybkox Date: Thu, 19 Dec 2024 11:52:34 +0100 Subject: [PATCH 23/23] fix: update triggers typing to cover all oracle timing options --- packages/types/dbinfo.d.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 06eecd8a1..d5b736561 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -140,7 +140,18 @@ export interface TriggerInfo extends SqlObjectInfo { objectId: string; functionName?: string; tableName?: string; - triggerTiming?: 'BEFORE' | 'AFTER' | 'INSTEAD OF' | null; + triggerTiming?: + | 'BEFORE' + | 'AFTER' + | 'INSTEAD OF' + | 'BEFORE EACH ROW' + | 'INSTEAD OF' + | 'AFTER EACH ROW' + | 'AFTER STATEMENT' + | 'BEFORE STATEMENT' + | 'AFTER EVENT' + | 'BEFORE EVENT' + | null; triggerLevel?: 'ROW' | 'STATEMENT'; eventType?: 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE'; }