mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-29 13:23:58 +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({
|
const obj1Match = expect.objectContaining({
|
||||||
pureName: 'obj1',
|
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',
|
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',
|
label: 'SQL Server',
|
||||||
@@ -349,6 +417,42 @@ end;$$`,
|
|||||||
supportRenameSqlObject: true,
|
supportRenameSqlObject: true,
|
||||||
defaultSchemaName: 'dbo',
|
defaultSchemaName: 'dbo',
|
||||||
// skipSeparateSchemas: true,
|
// 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',
|
label: 'SQLite',
|
||||||
@@ -429,25 +533,51 @@ end;$$`,
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'functions',
|
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',
|
create1:
|
||||||
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',
|
'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',
|
drop1: 'DROP FUNCTION ~obj1',
|
||||||
drop2: 'DROP FUNCTION ~obj2',
|
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 = [
|
const filterLocal = [
|
||||||
// filter local testing
|
// filter local testing
|
||||||
'-MySQL',
|
'-MySQL',
|
||||||
'-MariaDB',
|
// '-MariaDB',
|
||||||
'-PostgreSQL',
|
'-PostgreSQL',
|
||||||
'-SQL Server',
|
'SQL Server',
|
||||||
'-SQLite',
|
// '-SQLite',
|
||||||
'-CockroachDB',
|
// '-CockroachDB',
|
||||||
'-ClickHouse',
|
// '-ClickHouse',
|
||||||
'Oracle',
|
// 'Oracle',
|
||||||
];
|
];
|
||||||
|
|
||||||
const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL');
|
const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL');
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ export function databaseInfoFromYamlModel(filesOrDbInfo: DatabaseModelFile[] | D
|
|||||||
|
|
||||||
if (file.name.endsWith('.trigger.sql')) {
|
if (file.name.endsWith('.trigger.sql')) {
|
||||||
model.triggers.push({
|
model.triggers.push({
|
||||||
|
objectId: `triggers:${file.name.slice(0, -'.trigger.sql'.length)}`,
|
||||||
pureName: file.name.slice(0, -'.trigger.sql'.length),
|
pureName: file.name.slice(0, -'.trigger.sql'.length),
|
||||||
createSql: file.text,
|
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;
|
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 {
|
export interface SchemaInfo {
|
||||||
objectId?: string;
|
objectId?: string;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
procedures: 'img procedure',
|
procedures: 'img procedure',
|
||||||
functions: 'img function',
|
functions: 'img function',
|
||||||
queries: 'img query-data',
|
queries: 'img query-data',
|
||||||
|
triggers: 'icon trigger',
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultTabs = {
|
const defaultTabs = {
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
queries: 'QueryDataTab',
|
queries: 'QueryDataTab',
|
||||||
procedures: 'SqlObjectTab',
|
procedures: 'SqlObjectTab',
|
||||||
functions: 'SqlObjectTab',
|
functions: 'SqlObjectTab',
|
||||||
|
triggers: 'SqlObjectTab',
|
||||||
};
|
};
|
||||||
|
|
||||||
function createScriptTemplatesSubmenu(objectTypeField) {
|
function createScriptTemplatesSubmenu(objectTypeField) {
|
||||||
@@ -342,40 +344,8 @@
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
case 'functions':
|
case 'functions':
|
||||||
return [
|
case 'triggers':
|
||||||
...defaultDatabaseObjectAppObjectActions['functions'],
|
return [...defaultDatabaseObjectAppObjectActions['triggers']];
|
||||||
{
|
|
||||||
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 'collections':
|
case 'collections':
|
||||||
return [
|
return [
|
||||||
...defaultDatabaseObjectAppObjectActions['collections'],
|
...defaultDatabaseObjectAppObjectActions['collections'],
|
||||||
@@ -753,7 +723,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField];
|
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField] ?? [];
|
||||||
|
|
||||||
const configuredActionId = getLastUsedDefaultActions()[objectTypeField];
|
const configuredActionId = getLastUsedDefaultActions()[objectTypeField];
|
||||||
const prefferedAction =
|
const prefferedAction =
|
||||||
@@ -979,6 +949,10 @@
|
|||||||
|
|
||||||
function getExtInfo(data) {
|
function getExtInfo(data) {
|
||||||
const res = [];
|
const res = [];
|
||||||
|
if (data.objectTypeField === 'triggers') {
|
||||||
|
res.push(`${data.triggerTiming ?? ''} ${data.eventType ?? ''}`.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
if (data.objectComment) {
|
if (data.objectComment) {
|
||||||
res.push(data.objectComment);
|
res.push(data.objectComment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ export const defaultDatabaseObjectAppObjectActions = {
|
|||||||
icon: 'img sql-file',
|
icon: 'img sql-file',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
triggers: [
|
||||||
|
{
|
||||||
|
label: 'Show SQL',
|
||||||
|
tab: 'SqlObjectTab',
|
||||||
|
defaultActionId: 'showSql',
|
||||||
|
icon: 'img sql-file',
|
||||||
|
},
|
||||||
|
],
|
||||||
collections: [
|
collections: [
|
||||||
{
|
{
|
||||||
label: 'Open data',
|
label: 'Open data',
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
'icon add-folder': 'mdi mdi-folder-plus-outline',
|
'icon add-folder': 'mdi mdi-folder-plus-outline',
|
||||||
'icon add-column': 'mdi mdi-table-column-plus-after',
|
'icon add-column': 'mdi mdi-table-column-plus-after',
|
||||||
'icon parameter': 'mdi mdi-at',
|
'icon parameter': 'mdi mdi-at',
|
||||||
|
'icon trigger': 'mdi mdi-lightning-bolt',
|
||||||
|
|
||||||
'icon window-restore': 'mdi mdi-window-restore',
|
'icon window-restore': 'mdi mdi-window-restore',
|
||||||
'icon window-maximize': 'mdi mdi-window-maximize',
|
'icon window-maximize': 'mdi mdi-window-maximize',
|
||||||
|
|||||||
@@ -166,6 +166,13 @@ export function getSupportedScriptTemplates(objectTypeField: string): { label: s
|
|||||||
scriptTemplate: 'CALL OBJECT',
|
scriptTemplate: 'CALL OBJECT',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
case 'triggers':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'CREATE TRIGGER',
|
||||||
|
scriptTemplate: 'CREATE OBJECT',
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
// $: console.log('OBJECTS', $objects);
|
// $: console.log('OBJECTS', $objects);
|
||||||
|
|
||||||
$: objectList = _.flatten([
|
$: objectList = _.flatten([
|
||||||
...['tables', 'collections', 'views', 'matviews', 'procedures', 'functions'].map(objectTypeField =>
|
...['tables', 'collections', 'views', 'matviews', 'procedures', 'functions', 'triggers'].map(objectTypeField =>
|
||||||
_.sortBy(
|
_.sortBy(
|
||||||
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
||||||
['schemaName', 'pureName']
|
['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 procedureParameterRows = await this.analyserQuery('proceduresParameters');
|
||||||
const functionParameterRows = await this.analyserQuery('functionParameters');
|
const functionParameterRows = await this.analyserQuery('functionParameters');
|
||||||
|
|
||||||
|
this.feedback({ analysingMessage: 'Loading triggers' });
|
||||||
|
const triggerRows = await this.analyserQuery('triggers');
|
||||||
|
|
||||||
this.feedback({ analysingMessage: 'Loading view columns' });
|
this.feedback({ analysingMessage: 'Loading view columns' });
|
||||||
const viewColumnRows = await this.analyserQuery('viewColumns', ['views']);
|
const viewColumnRows = await this.analyserQuery('viewColumns', ['views']);
|
||||||
|
|
||||||
@@ -214,12 +217,24 @@ class MsSqlAnalyser extends DatabaseAnalyser {
|
|||||||
parameters: functionToParameters[row.objectId],
|
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 });
|
this.feedback({ analysingMessage: null });
|
||||||
return {
|
return {
|
||||||
tables,
|
tables,
|
||||||
views,
|
views,
|
||||||
procedures,
|
procedures,
|
||||||
functions,
|
functions,
|
||||||
|
triggers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const functionParameters = require('./functionParameters');
|
|||||||
const viewColumns = require('./viewColumns');
|
const viewColumns = require('./viewColumns');
|
||||||
const indexes = require('./indexes');
|
const indexes = require('./indexes');
|
||||||
const indexcols = require('./indexcols');
|
const indexcols = require('./indexcols');
|
||||||
|
const triggers = require('./triggers');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
columns,
|
columns,
|
||||||
@@ -28,4 +29,5 @@ module.exports = {
|
|||||||
indexes,
|
indexes,
|
||||||
indexcols,
|
indexcols,
|
||||||
tableSizes,
|
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' });
|
this.feedback({ analysingMessage: 'Loading indexes' });
|
||||||
const indexes = await this.analyserQuery('indexes', ['tables']);
|
const indexes = await this.analyserQuery('indexes', ['tables']);
|
||||||
this.feedback({ analysingMessage: 'Loading uniques' });
|
this.feedback({ analysingMessage: 'Loading uniques' });
|
||||||
|
|
||||||
|
this.feedback({ analysingMessage: 'Loading triggers' });
|
||||||
|
const triggers = await this.analyserQuery('triggers');
|
||||||
|
|
||||||
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
|
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
|
||||||
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
||||||
|
|
||||||
@@ -236,6 +240,15 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
contentHash: _.isDate(x.modifyDate) ? x.modifyDate.toISOString() : x.modifyDate,
|
contentHash: _.isDate(x.modifyDate) ? x.modifyDate.toISOString() : x.modifyDate,
|
||||||
parameters: functionNameToParameters[x.pureName],
|
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 });
|
this.feedback({ analysingMessage: null });
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const functionModifications = require('./functionModifications');
|
|||||||
const uniqueNames = require('./uniqueNames');
|
const uniqueNames = require('./uniqueNames');
|
||||||
const viewTexts = require('./viewTexts');
|
const viewTexts = require('./viewTexts');
|
||||||
const parameters = require('./parameters');
|
const parameters = require('./parameters');
|
||||||
|
const triggers = require('./triggers');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
columns,
|
columns,
|
||||||
@@ -26,4 +27,5 @@ module.exports = {
|
|||||||
indexes,
|
indexes,
|
||||||
uniqueNames,
|
uniqueNames,
|
||||||
viewTexts,
|
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' });
|
this.feedback({ analysingMessage: 'Loading indexes' });
|
||||||
const indexes = await this.analyserQuery('indexes', ['tables'], { $owner: this.dbhan.database });
|
const indexes = await this.analyserQuery('indexes', ['tables'], { $owner: this.dbhan.database });
|
||||||
this.feedback({ analysingMessage: 'Loading unique names' });
|
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 });
|
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables'], { $owner: this.dbhan.database });
|
||||||
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
||||||
|
|
||||||
@@ -183,6 +187,15 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
// schemaName: func.schema_name,
|
// schemaName: func.schema_name,
|
||||||
contentHash: func.hash_code,
|
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 });
|
this.feedback({ analysingMessage: null });
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const views = require('./views');
|
|||||||
const matviews = require('./matviews');
|
const matviews = require('./matviews');
|
||||||
const routines = require('./routines');
|
const routines = require('./routines');
|
||||||
const indexes = require('./indexes'); // use mysql
|
const indexes = require('./indexes'); // use mysql
|
||||||
|
const triggers = require('./triggers');
|
||||||
//const indexcols = require('./indexcols');
|
//const indexcols = require('./indexcols');
|
||||||
const uniqueNames = require('./uniqueNames');
|
const uniqueNames = require('./uniqueNames');
|
||||||
//const geometryColumns = require('./geometryColumns');
|
//const geometryColumns = require('./geometryColumns');
|
||||||
@@ -24,7 +25,8 @@ module.exports = {
|
|||||||
routines,
|
routines,
|
||||||
matviews,
|
matviews,
|
||||||
indexes,
|
indexes,
|
||||||
// indexcols,
|
triggers,
|
||||||
|
// indexcols,
|
||||||
uniqueNames,
|
uniqueNames,
|
||||||
//geometryColumns,
|
//geometryColumns,
|
||||||
//geographyColumns,
|
//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;
|
// if (!cntBase || !cntRef) continue;
|
||||||
const baseCols = _.sortBy(
|
const baseCols = _.sortBy(
|
||||||
fk_keyColumnUsage.rows.filter(
|
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'
|
'ordinal_position'
|
||||||
);
|
);
|
||||||
const refCols = _.sortBy(
|
const refCols = _.sortBy(
|
||||||
fk_keyColumnUsage.rows.filter(
|
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'
|
'ordinal_position'
|
||||||
);
|
);
|
||||||
@@ -185,6 +191,9 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
geographyColumns = await this.analyserQuery('geographyColumns', ['tables']);
|
geographyColumns = await this.analyserQuery('geographyColumns', ['tables']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.feedback({ analysingMessage: 'Loading triggers' });
|
||||||
|
const triggers = await this.analyserQuery('triggers');
|
||||||
|
|
||||||
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
this.feedback({ analysingMessage: 'Finalizing DB structure' });
|
||||||
|
|
||||||
const columnColumnsMapped = fkColumns.rows.map(x => ({
|
const columnColumnsMapped = fkColumns.rows.map(x => ({
|
||||||
@@ -348,6 +357,19 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
parameters: functionNameToParameters[`${func.schema_name}.${func.pure_name}`],
|
parameters: functionNameToParameters[`${func.schema_name}.${func.pure_name}`],
|
||||||
returnType: func.data_type,
|
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 });
|
this.feedback({ analysingMessage: null });
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ const geometryColumns = require('./geometryColumns');
|
|||||||
const geographyColumns = require('./geographyColumns');
|
const geographyColumns = require('./geographyColumns');
|
||||||
const proceduresParameters = require('./proceduresParameters');
|
const proceduresParameters = require('./proceduresParameters');
|
||||||
const foreignKeys = require('./foreignKeys');
|
const foreignKeys = require('./foreignKeys');
|
||||||
|
const triggers = require('./triggers');
|
||||||
|
|
||||||
const fk_keyColumnUsage = require('./fk_key_column_usage');
|
const fk_keyColumnUsage = require('./fk_key_column_usage');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -38,4 +40,5 @@ module.exports = {
|
|||||||
geometryColumns,
|
geometryColumns,
|
||||||
geographyColumns,
|
geographyColumns,
|
||||||
proceduresParameters,
|
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