mirror of
https://github.com/Fabio286/antares.git
synced 2025-04-24 23:08:42 +02:00
feat: stored routines edit
This commit is contained in:
parent
d695c9f8d2
commit
82fdc0bcd7
@ -2,6 +2,7 @@ import connection from './connection';
|
|||||||
import tables from './tables';
|
import tables from './tables';
|
||||||
import views from './views';
|
import views from './views';
|
||||||
import triggers from './triggers';
|
import triggers from './triggers';
|
||||||
|
import routines from './routines';
|
||||||
import updates from './updates';
|
import updates from './updates';
|
||||||
import application from './application';
|
import application from './application';
|
||||||
import database from './database';
|
import database from './database';
|
||||||
@ -14,6 +15,7 @@ export default () => {
|
|||||||
tables(connections);
|
tables(connections);
|
||||||
views(connections);
|
views(connections);
|
||||||
triggers(connections);
|
triggers(connections);
|
||||||
|
routines(connections);
|
||||||
database(connections);
|
database(connections);
|
||||||
users(connections);
|
users(connections);
|
||||||
updates();
|
updates();
|
||||||
|
43
src/main/ipc-handlers/routines.js
Normal file
43
src/main/ipc-handlers/routines.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-routine-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getRoutineInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -104,6 +104,20 @@ export class MySQLClient extends AntaresCore {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FUNCTIONS
|
||||||
|
const remappedFunctions = functions.filter(func => func.Db === db.Database).map(func => {
|
||||||
|
return {
|
||||||
|
name: func.Name,
|
||||||
|
type: func.Type,
|
||||||
|
definer: func.Definer,
|
||||||
|
created: func.Created,
|
||||||
|
updated: func.Modified,
|
||||||
|
comment: func.Comment,
|
||||||
|
charset: func.character_set_client,
|
||||||
|
security: func.Security_type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// SCHEDULERS
|
// SCHEDULERS
|
||||||
const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
||||||
return {
|
return {
|
||||||
@ -148,7 +162,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return {
|
return {
|
||||||
name: db.Database,
|
name: db.Database,
|
||||||
tables: remappedTables,
|
tables: remappedTables,
|
||||||
functions: functions.filter(func => func.Db === db.Database), // TODO: remap functions
|
functions: remappedFunctions,
|
||||||
procedures: remappedProcedures,
|
procedures: remappedProcedures,
|
||||||
triggers: remappedTriggers,
|
triggers: remappedTriggers,
|
||||||
schedulers: remappedSchedulers
|
schedulers: remappedSchedulers
|
||||||
@ -355,7 +369,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return results.rows.map(row => {
|
return results.rows.map(row => {
|
||||||
return {
|
return {
|
||||||
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
sql: row['SQL Original Statement'].match(/BEGIN(.*)END/gs)[0],
|
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
name: row.Trigger,
|
name: row.Trigger,
|
||||||
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||||
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||||
@ -405,9 +419,111 @@ export class MySQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async createTrigger (trigger) {
|
async createTrigger (trigger) {
|
||||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getRoutineInformations ({ schema, routine }) {
|
||||||
|
const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
const parameters = row['Create Procedure']
|
||||||
|
.match(/(?<=\().*?(?=\))/s)[0]
|
||||||
|
.replaceAll('\r', '')
|
||||||
|
.replaceAll('\t', '')
|
||||||
|
.split(',')
|
||||||
|
.map(el => {
|
||||||
|
const param = el.split(' ');
|
||||||
|
return {
|
||||||
|
name: param[1] ? param[1].replaceAll('`', '') : '',
|
||||||
|
type: param[2] ? param[2].replace(',', '') : '',
|
||||||
|
context: param[0] ? param[0].replace('\n', '') : ''
|
||||||
|
};
|
||||||
|
}).filter(el => el.name);
|
||||||
|
|
||||||
|
let dataAccess = 'CONTAINS SQL';
|
||||||
|
if (row['Create Procedure'].includes('NO SQL'))
|
||||||
|
dataAccess = 'NO SQL';
|
||||||
|
if (row['Create Procedure'].includes('READS SQL DATA'))
|
||||||
|
dataAccess = 'READS SQL DATA';
|
||||||
|
if (row['Create Procedure'].includes('MODIFIES SQL DATA'))
|
||||||
|
dataAccess = 'MODIFIES SQL DATA';
|
||||||
|
|
||||||
|
return {
|
||||||
|
definer: row['Create Procedure'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
sql: row['Create Procedure'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
|
parameters,
|
||||||
|
name: row.Procedure,
|
||||||
|
comment: row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
|
security: row['Create Procedure'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
||||||
|
deterministic: row['Create Procedure'].includes('DETERMINISTIC'),
|
||||||
|
dataAccess
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropRoutine (params) {
|
||||||
|
const sql = `DROP PROCEDURE \`${params.routine}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterRoutine (params) {
|
||||||
|
const { routine } = params;
|
||||||
|
const tempProcedure = Object.assign({}, routine);
|
||||||
|
tempProcedure.name = `Antares_${tempProcedure.name}_tmp`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createRoutine(tempProcedure);
|
||||||
|
await this.dropRoutine({ routine: tempProcedure.name });
|
||||||
|
await this.dropRoutine({ routine: routine.oldName });
|
||||||
|
await this.createRoutine(routine);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createRoutine (routine) {
|
||||||
|
const parameters = routine.parameters.reduce((acc, curr) => {
|
||||||
|
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}`);
|
||||||
|
return acc;
|
||||||
|
}, []).join(',');
|
||||||
|
|
||||||
|
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters})
|
||||||
|
LANGUAGE SQL
|
||||||
|
${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
|
${routine.dataAccess}
|
||||||
|
SQL SECURITY ${routine.security}
|
||||||
|
COMMENT '${routine.comment}'
|
||||||
|
${routine.sql}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SHOW COLLATION
|
* SHOW COLLATION
|
||||||
*
|
*
|
||||||
@ -709,6 +825,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @param {object} args
|
* @param {object} args
|
||||||
* @param {boolean} args.nest
|
* @param {boolean} args.nest
|
||||||
* @param {boolean} args.details
|
* @param {boolean} args.details
|
||||||
|
* @param {boolean} args.split
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
@ -716,13 +833,14 @@ export class MySQLClient extends AntaresCore {
|
|||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
details: false,
|
details: false,
|
||||||
|
split: true,
|
||||||
...args
|
...args
|
||||||
};
|
};
|
||||||
const nestTables = args.nest ? '.' : false;
|
const nestTables = args.nest ? '.' : false;
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
let selectedFields = [];
|
let selectedFields = [];
|
||||||
const queries = sql.split(';');
|
const queries = args.split ? sql.split(';') : [sql];
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||||
|
|
||||||
|
@ -89,7 +89,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -114,9 +113,6 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
|
||||||
selectedWorkspace: 'workspaces/getSelected'
|
|
||||||
}),
|
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
},
|
},
|
||||||
|
@ -83,6 +83,12 @@
|
|||||||
:connection="connection"
|
:connection="connection"
|
||||||
:trigger="workspace.breadcrumbs.trigger"
|
:trigger="workspace.breadcrumbs.trigger"
|
||||||
/>
|
/>
|
||||||
|
<WorkspacePropsTabRoutine
|
||||||
|
v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure"
|
||||||
|
:is-selected="selectedTab === 'prop'"
|
||||||
|
:connection="connection"
|
||||||
|
:routine="workspace.breadcrumbs.procedure"
|
||||||
|
/>
|
||||||
<WorkspaceTableTab
|
<WorkspaceTableTab
|
||||||
v-show="selectedTab === 'data'"
|
v-show="selectedTab === 'data'"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
@ -108,6 +114,7 @@ import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
|||||||
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
||||||
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
||||||
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||||
|
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
@ -117,7 +124,8 @@ export default {
|
|||||||
WorkspaceTableTab,
|
WorkspaceTableTab,
|
||||||
WorkspacePropsTab,
|
WorkspacePropsTab,
|
||||||
WorkspacePropsTabView,
|
WorkspacePropsTabView,
|
||||||
WorkspacePropsTabTrigger
|
WorkspacePropsTabTrigger,
|
||||||
|
WorkspacePropsTabRoutine
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
@ -144,6 +152,7 @@ export default {
|
|||||||
this.workspace.breadcrumbs.view === null &&
|
this.workspace.breadcrumbs.view === null &&
|
||||||
this.workspace.breadcrumbs.trigger === null &&
|
this.workspace.breadcrumbs.trigger === null &&
|
||||||
this.workspace.breadcrumbs.procedure === null &&
|
this.workspace.breadcrumbs.procedure === null &&
|
||||||
|
this.workspace.breadcrumbs.function === null &&
|
||||||
this.workspace.breadcrumbs.scheduler === null &&
|
this.workspace.breadcrumbs.scheduler === null &&
|
||||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||||
)
|
)
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
<div v-if="database.procedures.length" class="database-misc">
|
<div v-if="database.procedures.length" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
||||||
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
||||||
{{ $tc('word.storedRoutine', 2) }}
|
{{ $tc('word.storedRoutine', 2) }}
|
||||||
</summary>
|
</summary>
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
@ -82,7 +82,7 @@
|
|||||||
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
|
||||||
<span>{{ procedure.name }}</span>
|
<span>{{ procedure.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -92,6 +92,34 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="database.functions.length" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||||
|
{{ $tc('word.function', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="func of database.functions"
|
||||||
|
:key="func.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
||||||
|
<span>{{ func.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="database.schedulers.length" class="database-misc">
|
<div v-if="database.schedulers.length" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
<div class="context-element" @click="showCreateTriggerModal">
|
<div class="context-element" @click="showCreateTriggerModal">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element d-none" @click="false">
|
<div class="context-element" @click="false">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element d-none" @click="false">
|
<div class="context-element" @click="false">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="false">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
145
src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
Normal file
145
src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="optionsProxy.name"
|
||||||
|
class="form-input"
|
||||||
|
:class="{'is-error': !isTableNameValid}"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="optionsProxy.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.sqlSecurity') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.security" class="form-select">
|
||||||
|
<option>DEFINER</option>
|
||||||
|
<option>INVOKER</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.dataAccess') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.dataAccess" class="form-select">
|
||||||
|
<option>CONTAINS SQL</option>
|
||||||
|
<option>NO SQL</option>
|
||||||
|
<option>READS SQL DATA</option>
|
||||||
|
<option>MODIFIES SQL DATA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="optionsProxy.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsRoutineOptionsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isTableNameValid () {
|
||||||
|
return this.optionsProxy.name !== '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
if (!this.isTableNameValid)
|
||||||
|
this.optionsProxy.name = this.localOptions.name;
|
||||||
|
|
||||||
|
this.$emit('options-update', this.optionsProxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
287
src/renderer/components/WorkspacePropsRoutineParamsModal.vue
Normal file
287
src/renderer/components/WorkspacePropsRoutineParamsModal.vue
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="medium"
|
||||||
|
@confirm="confirmIndexesChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="columns col-gapless">
|
||||||
|
<div class="column col-5">
|
||||||
|
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||||
|
<div class="panel-header pt-0 pl-0">
|
||||||
|
<div class="d-flex">
|
||||||
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
|
<span>{{ $t('word.add') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-plus ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
@click.prevent="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||||
|
<div
|
||||||
|
v-for="param in parametersProxy"
|
||||||
|
:key="param.name"
|
||||||
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
|
:class="{'selected-param': selectedParam === param.name}"
|
||||||
|
@click="selectParameter($event, param.name)"
|
||||||
|
>
|
||||||
|
<div class="tile-icon">
|
||||||
|
<div>
|
||||||
|
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile-content">
|
||||||
|
<div class="tile-title">
|
||||||
|
{{ param.name }}
|
||||||
|
</div>
|
||||||
|
<small class="tile-subtitle text-gray">{{ param.type }} · {{ param.context }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="tile-action">
|
||||||
|
<button
|
||||||
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
|
:title="$t('word.delete')"
|
||||||
|
@click.prevent="removeParameter(param.name)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-close" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column col-7 pl-2 editor-col">
|
||||||
|
<form
|
||||||
|
v-if="selectedParamObj"
|
||||||
|
:style="{ height: modalInnerHeight + 'px'}"
|
||||||
|
class="form-horizontal"
|
||||||
|
>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.type') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="selectedParamObj.type" class="form-select text-uppercase">
|
||||||
|
<optgroup
|
||||||
|
v-for="group in workspace.dataTypes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="type in group.types"
|
||||||
|
:key="type.name"
|
||||||
|
:selected="selectedParamObj.type.toUpperCase() === type.name"
|
||||||
|
:value="type.name"
|
||||||
|
>
|
||||||
|
{{ type.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.context') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
name="context"
|
||||||
|
value="IN"
|
||||||
|
> <i class="form-icon" /> IN
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="OUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> OUT
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="INOUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> INOUT
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div v-if="!parametersProxy.length" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
||||||
|
</div>
|
||||||
|
<p class="empty-title h5">
|
||||||
|
{{ $t('message.thereAreNoParameters') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-primary" @click="addParameter">
|
||||||
|
{{ $t('message.createNewParameter') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsRoutineParamsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localParameters: Array,
|
||||||
|
routine: String,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
parametersProxy: [],
|
||||||
|
isOptionsChanging: false,
|
||||||
|
selectedParam: '',
|
||||||
|
modalInnerHeight: 400,
|
||||||
|
i: 1
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedParamObj () {
|
||||||
|
return this.parametersProxy.find(param => param.name === this.selectedParam);
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
this.getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmIndexesChange () {
|
||||||
|
this.$emit('parameters-update', this.parametersProxy);
|
||||||
|
},
|
||||||
|
selectParameter (event, name) {
|
||||||
|
if (this.selectedParam !== name && !event.target.classList.contains('remove-field'))
|
||||||
|
this.selectedParam = name;
|
||||||
|
},
|
||||||
|
getModalInnerHeight () {
|
||||||
|
const modalBody = document.querySelector('.modal-body');
|
||||||
|
if (modalBody)
|
||||||
|
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
|
},
|
||||||
|
addParameter () {
|
||||||
|
this.parametersProxy = [...this.parametersProxy, {
|
||||||
|
name: `Param${this.i++}`,
|
||||||
|
type: 'INT',
|
||||||
|
context: 'IN'
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (this.parametersProxy.length === 1)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
removeParameter (name) {
|
||||||
|
this.parametersProxy = this.parametersProxy.filter(param => param.name !== name);
|
||||||
|
|
||||||
|
if (this.selectedParam === name && this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (!this.parametersProxy.some(param => param.name === this.selectedParam))
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
resetSelectedID () {
|
||||||
|
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tile {
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: background 0.2s;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-color-light;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected-param {
|
||||||
|
background: $bg-color-light;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-col {
|
||||||
|
border-left: 2px solid $bg-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-field .mdi {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
262
src/renderer/components/WorkspacePropsTabRoutine.vue
Normal file
262
src/renderer/components/WorkspacePropsTabRoutine.vue
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="isChanged"
|
||||||
|
@click="false"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.run') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-play ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
|
<span>{{ $t('word.parameters') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<span>{{ $t('word.options') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2">
|
||||||
|
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localRoutine.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsRoutineOptionsModal
|
||||||
|
v-if="isOptionsModal"
|
||||||
|
:local-options="localRoutine"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideOptionsModal"
|
||||||
|
@options-update="optionsUpdate"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsRoutineParamsModal
|
||||||
|
v-if="isParamsModal"
|
||||||
|
:local-parameters="localRoutine.parameters"
|
||||||
|
:workspace="workspace"
|
||||||
|
:routine="localRoutine.name"
|
||||||
|
@hide="hideParamsModal"
|
||||||
|
@parameters-update="parametersUpdate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal';
|
||||||
|
import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal';
|
||||||
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabRoutine',
|
||||||
|
components: {
|
||||||
|
QueryEditor,
|
||||||
|
WorkspacePropsRoutineOptionsModal,
|
||||||
|
WorkspacePropsRoutineParamsModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
routine: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isQuering: false,
|
||||||
|
isSaving: false,
|
||||||
|
isOptionsModal: false,
|
||||||
|
isParamsModal: false,
|
||||||
|
originalRoutine: null,
|
||||||
|
localRoutine: { sql: '' },
|
||||||
|
lastRoutine: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalRoutine ? this.workspace.users.some(user => this.originalRoutine.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async routine () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastRoutine !== this.routine) {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getRoutineData () {
|
||||||
|
if (!this.routine) return;
|
||||||
|
this.isQuering = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
routine: this.workspace.breadcrumbs.procedure
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalRoutine = response;
|
||||||
|
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
||||||
|
this.sqlProxy = this.localRoutine.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isQuering = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
routine: {
|
||||||
|
...this.localRoutine,
|
||||||
|
oldName: this.originalRoutine.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.alterRoutine(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalRoutine.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localRoutine.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getRoutineData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optionsUpdate (options) {
|
||||||
|
this.localRoutine = options;
|
||||||
|
},
|
||||||
|
parametersUpdate (parameters) {
|
||||||
|
this.localRoutine = { ...this.localRoutine, parameters };
|
||||||
|
},
|
||||||
|
showOptionsModal () {
|
||||||
|
this.isOptionsModal = true;
|
||||||
|
},
|
||||||
|
hideOptionsModal () {
|
||||||
|
this.isOptionsModal = false;
|
||||||
|
},
|
||||||
|
showParamsModal () {
|
||||||
|
this.isParamsModal = true;
|
||||||
|
},
|
||||||
|
hideParamsModal () {
|
||||||
|
this.isParamsModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -74,7 +74,11 @@ module.exports = {
|
|||||||
trigger: 'Trigger | Triggers',
|
trigger: 'Trigger | Triggers',
|
||||||
storedRoutine: 'Stored routine | Stored routines',
|
storedRoutine: 'Stored routine | Stored routines',
|
||||||
scheduler: 'Scheduler | Schedulers',
|
scheduler: 'Scheduler | Schedulers',
|
||||||
event: 'Event'
|
event: 'Event',
|
||||||
|
parameters: 'Parameters',
|
||||||
|
function: 'Function | Functions',
|
||||||
|
deterministic: 'Deterministic',
|
||||||
|
context: 'Context'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@ -148,7 +152,11 @@ module.exports = {
|
|||||||
createNewView: 'Create new view',
|
createNewView: 'Create new view',
|
||||||
deleteTrigger: 'Delete trigger',
|
deleteTrigger: 'Delete trigger',
|
||||||
createNewTrigger: 'Create new trigger',
|
createNewTrigger: 'Create new trigger',
|
||||||
currentUser: 'Current user'
|
currentUser: 'Current user',
|
||||||
|
routineBody: 'Routine body',
|
||||||
|
dataAccess: 'Data access',
|
||||||
|
thereAreNoParameters: 'There are no parameters',
|
||||||
|
createNewParameter: 'Create new parameter'
|
||||||
},
|
},
|
||||||
// Date and Time
|
// Date and Time
|
||||||
short: {
|
short: {
|
||||||
|
20
src/renderer/ipc-api/Routines.js
Normal file
20
src/renderer/ipc-api/Routines.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getRoutineInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-routine-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('drop-routine', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('alter-routine', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('create-routine', params);
|
||||||
|
}
|
||||||
|
}
|
@ -345,6 +345,7 @@ export default {
|
|||||||
table: null,
|
table: null,
|
||||||
trigger: null,
|
trigger: null,
|
||||||
procedure: null,
|
procedure: null,
|
||||||
|
function: null,
|
||||||
scheduler: null,
|
scheduler: null,
|
||||||
view: null
|
view: null
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user