Skip to content

Commit

Permalink
feat: stored routines edit
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabio286 committed Jan 5, 2021
1 parent d695c9f commit 82fdc0b
Show file tree
Hide file tree
Showing 13 changed files with 937 additions and 15 deletions.
2 changes: 2 additions & 0 deletions src/main/ipc-handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import connection from './connection';
import tables from './tables';
import views from './views';
import triggers from './triggers';
import routines from './routines';
import updates from './updates';
import application from './application';
import database from './database';
Expand All @@ -14,6 +15,7 @@ export default () => {
tables(connections);
views(connections);
triggers(connections);
routines(connections);
database(connections);
users(connections);
updates();
Expand Down
43 changes: 43 additions & 0 deletions src/main/ipc-handlers/routines.js
Original file line number Diff line number Diff line change
@@ -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() };
}
});
};
124 changes: 121 additions & 3 deletions src/main/libs/clients/MySQLClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
return {
Expand Down Expand Up @@ -148,7 +162,7 @@ export class MySQLClient extends AntaresCore {
return {
name: db.Database,
tables: remappedTables,
functions: functions.filter(func => func.Db === db.Database), // TODO: remap functions
functions: remappedFunctions,
procedures: remappedProcedures,
triggers: remappedTriggers,
schedulers: remappedSchedulers
Expand Down Expand Up @@ -355,7 +369,7 @@ export class MySQLClient extends AntaresCore {
return results.rows.map(row => {
return {
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,
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
Expand Down Expand Up @@ -405,9 +419,111 @@ export class MySQLClient extends AntaresCore {
*/
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}`;
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);
}

/**
* 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
*
Expand Down Expand Up @@ -709,20 +825,22 @@ export class MySQLClient extends AntaresCore {
* @param {object} args
* @param {boolean} args.nest
* @param {boolean} args.details
* @param {boolean} args.split
* @returns {Promise}
* @memberof MySQLClient
*/
async raw (sql, args) {
args = {
nest: false,
details: false,
split: true,
...args
};
const nestTables = args.nest ? '.' : false;
const resultsArr = [];
let paramsArr = [];
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

Expand Down
4 changes: 0 additions & 4 deletions src/renderer/components/ModalNewTrigger.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@
</template>

<script>
import { mapGetters } from 'vuex';
import ConfirmModal from '@/components/BaseConfirmModal';
export default {
Expand All @@ -114,9 +113,6 @@ export default {
};
},
computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected'
}),
schema () {
return this.workspace.breadcrumbs.schema;
},
Expand Down
11 changes: 10 additions & 1 deletion src/renderer/components/Workspace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@
:connection="connection"
:trigger="workspace.breadcrumbs.trigger"
/>
<WorkspacePropsTabRoutine
v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:routine="workspace.breadcrumbs.procedure"
/>
<WorkspaceTableTab
v-show="selectedTab === 'data'"
:connection="connection"
Expand All @@ -108,6 +114,7 @@ import WorkspaceTableTab from '@/components/WorkspaceTableTab';
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
export default {
name: 'Workspace',
Expand All @@ -117,7 +124,8 @@ export default {
WorkspaceTableTab,
WorkspacePropsTab,
WorkspacePropsTabView,
WorkspacePropsTabTrigger
WorkspacePropsTabTrigger,
WorkspacePropsTabRoutine
},
props: {
connection: Object
Expand All @@ -144,6 +152,7 @@ export default {
this.workspace.breadcrumbs.view === null &&
this.workspace.breadcrumbs.trigger === null &&
this.workspace.breadcrumbs.procedure === null &&
this.workspace.breadcrumbs.function === null &&
this.workspace.breadcrumbs.scheduler === null &&
['data', 'prop'].includes(this.workspace.selected_tab)
)
Expand Down
32 changes: 30 additions & 2 deletions src/renderer/components/WorkspaceExploreBarDatabase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<div v-if="database.procedures.length" class="database-misc">
<details class="accordion">
<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) }}
</summary>
<div class="accordion-body">
Expand All @@ -82,7 +82,7 @@
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
>
<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>
</a>
</li>
Expand All @@ -92,6 +92,34 @@
</details>
</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">
<details class="accordion">
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
<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>
</div>
<div class="context-element d-none" @click="false">
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
<div class="context-element" @click="false">
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
</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>
</div>
</div>
Expand Down
Loading

0 comments on commit 82fdc0b

Please sign in to comment.