Merge pull request #969 from dbgate/feature/triggers

Feature/triggers
This commit is contained in:
Jan Prochazka
2024-12-19 12:09:08 +01:00
committed by GitHub
22 changed files with 402 additions and 48 deletions

View File

@@ -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,12 +217,24 @@ class MsSqlAnalyser extends DatabaseAnalyser {
parameters: functionToParameters[row.objectId],
}));
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,
pureName: row.triggerName,
}));
this.feedback({ analysingMessage: null });
return {
tables,
views,
procedures,
functions,
triggers,
};
}

View File

@@ -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,
};

View File

@@ -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 eventType,
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
`;

View File

@@ -160,6 +160,10 @@ class Analyser extends DatabaseAnalyser {
this.feedback({ analysingMessage: 'Loading indexes' });
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']);
this.feedback({ analysingMessage: 'Finalizing DB structure' });
@@ -236,6 +240,15 @@ class Analyser extends DatabaseAnalyser {
contentHash: _.isDate(x.modifyDate) ? x.modifyDate.toISOString() : x.modifyDate,
parameters: functionNameToParameters[x.pureName],
})),
triggers: triggers.rows.map(row => ({
contentHash: row.modifyDate,
pureName: row.triggerName,
eventType: row.eventType,
triggerTiming: row.triggerTiming,
schemaName: row.schemaName,
tableName: row.tableName,
createSql: row.definition,
})),
};
this.feedback({ analysingMessage: null });
return res;

View File

@@ -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,
};

View File

@@ -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
`;

View File

@@ -71,6 +71,10 @@ 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 });
this.feedback({ analysingMessage: 'Loading triggers' });
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables'], { $owner: this.dbhan.database });
this.feedback({ analysingMessage: 'Finalizing DB structure' });
@@ -183,6 +187,15 @@ class Analyser extends DatabaseAnalyser {
// schemaName: func.schema_name,
contentHash: func.hash_code,
})),
triggers: triggers.rows.map(row => ({
pureName: row.TRIGGER_NAME,
trigerName: row.TRIGGER_NAME,
definition: row.DEFINITION,
tableName: row.TABLE_NAME,
triggerLevel: row.TRIGGER_LEVEL,
triggerTiming: row.TRIGGER_TIMING,
eventType: row.EVENT_TYPE,
})),
};
this.feedback({ analysingMessage: null });

View File

@@ -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,

View File

@@ -0,0 +1,19 @@
module.exports = `
SELECT
TRIGGER_TYPE AS trigger_timing,
TRIGGERING_EVENT 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
`;

View File

@@ -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'
);
@@ -185,6 +191,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 => ({
@@ -348,6 +357,19 @@ class Analyser extends DatabaseAnalyser {
parameters: functionNameToParameters[`${func.schema_name}.${func.pure_name}`],
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,
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 });

View File

@@ -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,
};

View File

@@ -0,0 +1,35 @@
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,
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 OF' 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
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
`;