mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 14:06:00 +00:00
@@ -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,26 @@ describe('Object analyse', () => {
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
test.each(flatSourceTriggers())(
|
||||
'Test triggers - %s - %s',
|
||||
testWrapper(async (conn, driver, trigger) => {
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
const { triggerOtherDropSql, triggerOtherCreateSql, create, drop, expected, objectTypeField } = trigger;
|
||||
|
||||
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));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
console.log(flatSourceTriggers());
|
||||
|
||||
@@ -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',
|
||||
@@ -264,6 +288,50 @@ end;$$`,
|
||||
],
|
||||
},
|
||||
],
|
||||
triggers: [
|
||||
{
|
||||
testName: 'triggers after each row',
|
||||
create: `CREATE TRIGGER obj1
|
||||
AFTER INSERT ON t1
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION test_function();
|
||||
`,
|
||||
drop: 'DROP TRIGGER obj1 ON t1;',
|
||||
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 ON t1;',
|
||||
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',
|
||||
@@ -349,6 +417,42 @@ end;$$`,
|
||||
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',
|
||||
@@ -429,25 +533,51 @@ 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: 'INSERT',
|
||||
triggerTiming: '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: 'INSERT',
|
||||
triggerTiming: 'BEFORE EACH ROW',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const filterLocal = [
|
||||
// filter local testing
|
||||
'-MySQL',
|
||||
'-MariaDB',
|
||||
// '-MariaDB',
|
||||
'-PostgreSQL',
|
||||
'-SQL Server',
|
||||
'-SQLite',
|
||||
'-CockroachDB',
|
||||
'-ClickHouse',
|
||||
'Oracle',
|
||||
'SQL Server',
|
||||
// '-SQLite',
|
||||
// '-CockroachDB',
|
||||
// '-ClickHouse',
|
||||
// 'Oracle',
|
||||
];
|
||||
|
||||
const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL');
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
20
packages/types/dbinfo.d.ts
vendored
20
packages/types/dbinfo.d.ts
vendored
@@ -136,7 +136,25 @@ export interface FunctionInfo extends CallableObjectInfo {
|
||||
returnType?: string;
|
||||
}
|
||||
|
||||
export interface TriggerInfo extends SqlObjectInfo {}
|
||||
export interface TriggerInfo extends SqlObjectInfo {
|
||||
objectId: string;
|
||||
functionName?: string;
|
||||
tableName?: string;
|
||||
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';
|
||||
}
|
||||
|
||||
export interface SchemaInfo {
|
||||
objectId?: string;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
procedures: 'img procedure',
|
||||
functions: 'img function',
|
||||
queries: 'img query-data',
|
||||
triggers: 'icon trigger',
|
||||
};
|
||||
|
||||
const defaultTabs = {
|
||||
@@ -51,6 +52,7 @@
|
||||
queries: 'QueryDataTab',
|
||||
procedures: 'SqlObjectTab',
|
||||
functions: 'SqlObjectTab',
|
||||
triggers: 'SqlObjectTab',
|
||||
};
|
||||
|
||||
function createScriptTemplatesSubmenu(objectTypeField) {
|
||||
@@ -342,40 +344,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'],
|
||||
@@ -753,7 +723,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField];
|
||||
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField] ?? [];
|
||||
|
||||
const configuredActionId = getLastUsedDefaultActions()[objectTypeField];
|
||||
const prefferedAction =
|
||||
@@ -979,6 +949,10 @@
|
||||
|
||||
function getExtInfo(data) {
|
||||
const res = [];
|
||||
if (data.objectTypeField === 'triggers') {
|
||||
res.push(`${data.triggerTiming ?? ''} ${data.eventType ?? ''}`.toLowerCase());
|
||||
}
|
||||
|
||||
if (data.objectComment) {
|
||||
res.push(data.objectComment);
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -166,6 +166,13 @@ export function getSupportedScriptTemplates(objectTypeField: string): { label: s
|
||||
scriptTemplate: 'CALL OBJECT',
|
||||
},
|
||||
];
|
||||
case 'triggers':
|
||||
return [
|
||||
{
|
||||
label: 'CREATE TRIGGER',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
@@ -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']
|
||||
|
||||
22
patches/ace-builds+1.33.1.patch
Normal file
22
patches/ace-builds+1.33.1.patch
Normal file
@@ -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 = /(?<!\[)(\bCASE\b|\bBEGIN\b)(?!\])|^\s*(\/\*)/i;
|
||||
this.startRegionRe = /^\s*(\/\*|--)#?region\b/;
|
||||
this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) {
|
||||
var line = session.getLine(row);
|
||||
@@ -337,7 +337,7 @@ oop.inherits(FoldMode, BaseFoldMode);
|
||||
var maxRow = session.getLength();
|
||||
var line;
|
||||
var depth = 1;
|
||||
- var re = /(\bCASE\b|\bBEGIN\b)|(\bEND\b)/i;
|
||||
+ var re = /(?<!\[)(\bCASE\b|\bBEGIN\b)|(\bEND\b)(?!\])/i;
|
||||
while (++row < maxRow) {
|
||||
line = session.getLine(row);
|
||||
var m = re.exec(line);
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
28
plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js
Normal file
28
plugins/dbgate-plugin-mssql/src/backend/sql/triggers.js
Normal 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
|
||||
`;
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
13
plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js
Normal file
13
plugins/dbgate-plugin-mysql/src/backend/sql/triggers.js
Normal 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
|
||||
`;
|
||||
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
|
||||
19
plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js
Normal file
19
plugins/dbgate-plugin-oracle/src/backend/sql/triggers.js
Normal 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
|
||||
`;
|
||||
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
35
plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js
Normal file
35
plugins/dbgate-plugin-postgres/src/backend/sql/triggers.js
Normal 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
|
||||
`;
|
||||
Reference in New Issue
Block a user