feat(PostgreSQL): enable/disable triggers from contextual menu

This commit is contained in:
Fabio Di Stasio 2021-11-04 21:54:42 +01:00
parent c00fd1381f
commit 534659f9ae
9 changed files with 110 additions and 27 deletions

View File

@ -72,6 +72,7 @@ module.exports = {
triggerTableInName: false, triggerTableInName: false,
triggerUpdateColumns: false, triggerUpdateColumns: false,
triggerOnlyRename: false, triggerOnlyRename: false,
triggerEnableDisable: false,
triggerFunctionSql: false, triggerFunctionSql: false,
triggerFunctionlanguages: false, triggerFunctionlanguages: false,
parametersLength: false, parametersLength: false,

View File

@ -50,5 +50,6 @@ module.exports = {
triggerMultipleEvents: true, triggerMultipleEvents: true,
triggerTableInName: true, triggerTableInName: true,
triggerOnlyRename: false, triggerOnlyRename: false,
triggerEnableDisable: true,
languages: ['sql', 'plpgsql', 'c', 'internal'] languages: ['sql', 'plpgsql', 'c', 'internal']
}; };

View File

@ -40,4 +40,19 @@ export default (connections) => {
return { status: 'error', response: err.toString() }; return { status: 'error', response: err.toString() };
} }
}); });
ipcMain.handle('toggle-trigger', async (event, params) => {
try {
if (!params.enabled)
await connections[params.uid].enableTrigger(params);
else
await connections[params.uid].disableTrigger(params);
return { status: 'success' };
}
catch (err) {
return { status: 'error', response: err.toString() };
}
});
}; };

View File

@ -168,19 +168,20 @@ export class PostgreSQLClient extends AntaresCore {
} }
let { rows: triggers } = await this.raw(` let { rows: triggers } = await this.raw(`
SELECT event_object_schema AS table_schema, SELECT
event_object_table AS table_name, pg_class.relname AS table_name,
trigger_schema, pg_trigger.tgname AS trigger_name,
trigger_name, pg_namespace.nspname AS trigger_schema,
string_agg(event_manipulation, ',') AS event, (pg_trigger.tgenabled != 'D')::bool AS enabled
action_timing AS activation, FROM pg_trigger
action_condition AS condition, JOIN pg_class ON pg_trigger.tgrelid = pg_class.oid
action_statement AS definition JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
FROM information_schema.triggers JOIN information_schema.triggers ON information_schema.triggers.trigger_schema = pg_namespace.nspname
AND information_schema.triggers.event_object_table = pg_class.relname
AND information_schema.triggers.trigger_name = pg_trigger.tgname
WHERE trigger_schema = '${db.database}' WHERE trigger_schema = '${db.database}'
GROUP BY 1,2,3,4,6,7,8 GROUP BY 1, 2, 3, 4
ORDER BY table_schema, ORDER BY table_name
table_name
`); `);
if (triggers.length) { if (triggers.length) {
@ -239,12 +240,10 @@ export class PostgreSQLClient extends AntaresCore {
return { return {
name: `${trigger.table_name}.${trigger.trigger_name}`, name: `${trigger.table_name}.${trigger.trigger_name}`,
orgName: trigger.trigger_name, orgName: trigger.trigger_name,
timing: trigger.activation,
definer: '', definer: '',
definition: trigger.definition,
event: trigger.event,
table: trigger.table_name, table: trigger.table_name,
sqlMode: '' sqlMode: '',
enabled: trigger.enabled
}; };
}); });
@ -597,19 +596,25 @@ export class PostgreSQLClient extends AntaresCore {
const [table, triggerName] = trigger.split('.'); const [table, triggerName] = trigger.split('.');
const results = await this.raw(` const results = await this.raw(`
SELECT event_object_schema AS table_schema, SELECT
event_object_table AS table_name, information_schema.triggers.event_object_schema AS table_schema,
trigger_schema, information_schema.triggers.event_object_table AS table_name,
trigger_name, information_schema.triggers.trigger_schema,
string_agg(event_manipulation, ',') AS event, information_schema.triggers.trigger_name,
string_agg(event_manipulation, ',') AS EVENT,
action_timing AS activation, action_timing AS activation,
action_condition AS condition, action_condition AS condition,
action_statement AS definition action_statement AS definition,
FROM information_schema.triggers (pg_trigger.tgenabled != 'D')::bool AS enabled
FROM pg_trigger
JOIN pg_class ON pg_trigger.tgrelid = pg_class.oid
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
JOIN information_schema.triggers ON pg_namespace.nspname = information_schema.triggers.trigger_schema
AND pg_class.relname = information_schema.triggers.event_object_table
WHERE trigger_schema = '${schema}' WHERE trigger_schema = '${schema}'
AND trigger_name = '${triggerName}' AND trigger_name = '${triggerName}'
AND event_object_table = '${table}' AND event_object_table = '${table}'
GROUP BY 1,2,3,4,6,7,8 GROUP BY 1,2,3,4,6,7,8,9
ORDER BY table_schema, ORDER BY table_schema,
table_name table_name
`); `);
@ -619,7 +624,7 @@ export class PostgreSQLClient extends AntaresCore {
sql: row.definition, sql: row.definition,
name: row.trigger_name, name: row.trigger_name,
table: row.table_name, table: row.table_name,
event: row.event.split(','), event: [...new Set(row.event.split(','))],
activation: row.activation activation: row.activation
}; };
})[0]; })[0];
@ -671,6 +676,18 @@ export class PostgreSQLClient extends AntaresCore {
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
async enableTrigger ({ schema, trigger }) {
const [table, triggerName] = trigger.split('.');
const sql = `ALTER TABLE "${schema}"."${table}" ENABLE TRIGGER "${triggerName}"`;
return await this.raw(sql, { split: false });
}
async disableTrigger ({ schema, trigger }) {
const [table, triggerName] = trigger.split('.');
const sql = `ALTER TABLE "${schema}"."${table}" DISABLE TRIGGER "${triggerName}"`;
return await this.raw(sql, { split: false });
}
/** /**
* SHOW CREATE PROCEDURE * SHOW CREATE PROCEDURE
* *

View File

@ -10,6 +10,18 @@
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
</div> </div>
<div
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable"
class="context-element"
@click="toggleTrigger"
>
<span v-if="!selectedMisc.enabled" class="d-flex">
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.enable') }}
</span>
<span v-else class="d-flex">
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ $t('word.disable') }}
</span>
</div>
<div class="context-element" @click="showDeleteModal"> <div class="context-element" @click="showDeleteModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
</div> </div>
@ -78,6 +90,9 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
customizations () {
return this.getWorkspace(this.selectedWorkspace).customizations;
},
deleteMessage () { deleteMessage () {
switch (this.selectedMisc.type) { switch (this.selectedMisc.type) {
case 'trigger': case 'trigger':
@ -273,6 +288,24 @@ export default {
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true }); this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
this.closeContext(); this.closeContext();
},
async toggleTrigger () {
try {
const { status, response } = await Triggers.toggleTrigger({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
trigger: this.selectedMisc.name,
enabled: this.selectedMisc.enabled
});
if (status !== 'success')
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.closeContext();
this.$emit('reload');
} }
} }
}; };

View File

@ -71,6 +71,13 @@
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" /> <i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
<span v-html="highlightWord(trigger.name)" /> <span v-html="highlightWord(trigger.name)" />
</a> </a>
<div
v-if="trigger.enabled === false"
class="tooltip tooltip-left disabled-indicator"
:data-tooltip="$t('word.disabled')"
>
<i class="table-icon mdi mdi-pause mdi-18px mr-1" />
</div>
</li> </li>
</ul> </ul>
</div> </div>
@ -500,7 +507,8 @@ export default {
} }
} }
.table-size { .table-size,
.disabled-indicator {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;

View File

@ -121,7 +121,10 @@ module.exports = {
history: 'History', history: 'History',
select: 'Select', select: 'Select',
passphrase: 'Passphrase', passphrase: 'Passphrase',
filter: 'Filter' filter: 'Filter',
disabled: 'Disabled',
enable: 'Enable',
disable: 'Disable'
}, },
message: { message: {
appWelcome: 'Welcome to Antares SQL Client!', appWelcome: 'Welcome to Antares SQL Client!',

View File

@ -17,4 +17,8 @@ export default class {
static createTrigger (params) { static createTrigger (params) {
return ipcRenderer.invoke('create-trigger', params); return ipcRenderer.invoke('create-trigger', params);
} }
static toggleTrigger (params) {
return ipcRenderer.invoke('toggle-trigger', params);
}
} }

View File

@ -70,6 +70,7 @@
"bytea": $blob-color, "bytea": $blob-color,
"enum": $enum-color, "enum": $enum-color,
"set": $enum-color, "set": $enum-color,
"bool": $enum-color,
"boolean": $enum-color, "boolean": $enum-color,
"interval": $array-color, "interval": $array-color,
"array": $array-color, "array": $array-color,