mirror of https://github.com/Fabio286/antares.git
feat(MySQL): ability to cancel queries
This commit is contained in:
parent
e7a1858091
commit
a59f77f618
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard"
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-standard-scss"
|
||||
],
|
||||
"fix": true,
|
||||
"formatter": "verbose",
|
||||
|
|
|
@ -11,6 +11,7 @@ module.exports = {
|
|||
sslConnection: false,
|
||||
sshConnection: false,
|
||||
fileConnection: false,
|
||||
cancelQueries: false,
|
||||
// Tools
|
||||
processesList: false,
|
||||
usersManagement: false,
|
||||
|
|
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
engines: true,
|
||||
sslConnection: true,
|
||||
sshConnection: true,
|
||||
cancelQueries: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
|
|
|
@ -135,7 +135,7 @@ export default connections => {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema }) => {
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema, tabUid }) => {
|
||||
if (!query) return;
|
||||
|
||||
try {
|
||||
|
@ -143,6 +143,7 @@ export default connections => {
|
|||
nest: true,
|
||||
details: true,
|
||||
schema,
|
||||
tabUid,
|
||||
comments: false
|
||||
});
|
||||
|
||||
|
@ -152,4 +153,16 @@ export default connections => {
|
|||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('kill-tab-query', async (event, { uid, tabUid }) => {
|
||||
if (!tabUid) return;
|
||||
|
||||
try {
|
||||
await connections[uid].killTabQuery(tabUid);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ export class MySQLClient extends AntaresCore {
|
|||
super(args);
|
||||
|
||||
this._schema = null;
|
||||
this._runningConnections = new Map();
|
||||
|
||||
this.types = {
|
||||
0: 'DECIMAL',
|
||||
|
@ -1210,10 +1211,26 @@ export class MySQLClient extends AntaresCore {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killProcess (id) {
|
||||
return await this.raw(`KILL ${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killTabQuery (tabUid) {
|
||||
const id = this._runningConnections.get(tabUid);
|
||||
if (id)
|
||||
return await this.killProcess(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TABLE
|
||||
*
|
||||
|
@ -1535,6 +1552,9 @@ export class MySQLClient extends AntaresCore {
|
|||
const isPool = typeof this._connection.getConnection === 'function';
|
||||
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||
|
||||
if (args.tabUid && isPool)
|
||||
this._runningConnections.set(args.tabUid, connection.connection.connectionId);
|
||||
|
||||
if (args.schema)
|
||||
await connection.query(`USE \`${args.schema}\``);
|
||||
|
||||
|
@ -1595,7 +1615,10 @@ export class MySQLClient extends AntaresCore {
|
|||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
if (isPool) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
|
||||
|
@ -1604,7 +1627,10 @@ export class MySQLClient extends AntaresCore {
|
|||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
if (isPool) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
|
@ -1619,7 +1645,10 @@ export class MySQLClient extends AntaresCore {
|
|||
keys: keysArr
|
||||
});
|
||||
}).catch((err) => {
|
||||
if (isPool) connection.release();
|
||||
if (isPool) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
@ -1627,7 +1656,10 @@ export class MySQLClient extends AntaresCore {
|
|||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
if (isPool) connection.release();
|
||||
if (isPool) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
|
||||
tabindex="0"
|
||||
@keydown.116="runQuery(query)"
|
||||
@keydown.75="killTabQuery"
|
||||
@keydown.ctrl.alt.87="clear"
|
||||
@keydown.ctrl.66="beautify"
|
||||
@keydown.ctrl.71="openHistoryModal"
|
||||
|
@ -22,7 +23,19 @@
|
|||
<div ref="resizer" class="query-area-resizer" />
|
||||
<div class="workspace-query-runner-footer">
|
||||
<div class="workspace-query-buttons">
|
||||
<div @mouseenter="setCancelButtonVisibility(true)" @mouseleave="setCancelButtonVisibility(false)">
|
||||
<button
|
||||
v-if="showCancel && isQuering"
|
||||
class="btn btn-primary btn-sm cancellable"
|
||||
:disabled="!query"
|
||||
:title="$t('word.cancel')"
|
||||
@click="killTabQuery()"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-window-close" />
|
||||
<span class="d-invisible pr-1">{{ $t('word.run') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-primary btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
:disabled="!query"
|
||||
|
@ -32,6 +45,7 @@
|
|||
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||
<span>{{ $t('word.run') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-link btn-sm mr-0"
|
||||
:disabled="!query || isQuering"
|
||||
|
@ -110,7 +124,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<WorkspaceTabQueryEmptyState v-if="!results.length && !isQuering" />
|
||||
<WorkspaceTabQueryEmptyState v-if="!results.length && !isQuering" :customizations="workspace.customizations" />
|
||||
<div class="workspace-query-results p-relative column col-12">
|
||||
<BaseLoader v-if="isQuering" />
|
||||
<WorkspaceTabQueryTable
|
||||
|
@ -166,6 +180,8 @@ export default {
|
|||
query: '',
|
||||
lastQuery: '',
|
||||
isQuering: false,
|
||||
isCancelling: false,
|
||||
showCancel: false,
|
||||
results: [],
|
||||
selectedSchema: null,
|
||||
resultsCount: 0,
|
||||
|
@ -248,6 +264,7 @@ export default {
|
|||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.selectedSchema,
|
||||
tabUid: this.tab.uid,
|
||||
query
|
||||
};
|
||||
|
||||
|
@ -283,6 +300,29 @@ export default {
|
|||
this.isQuering = false;
|
||||
this.lastQuery = query;
|
||||
},
|
||||
async killTabQuery () {
|
||||
if (this.isCancelling) return;
|
||||
|
||||
this.isCancelling = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
tabUid: this.tab.uid
|
||||
};
|
||||
|
||||
await Schema.killTabQuery(params);
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isCancelling = false;
|
||||
},
|
||||
setCancelButtonVisibility (val) {
|
||||
if (this.workspace.customizations.cancelQueries)
|
||||
this.showCancel = val;
|
||||
},
|
||||
reloadTable () {
|
||||
this.runQuery(this.lastQuery);
|
||||
},
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
<div class="mb-4">
|
||||
{{ $t('message.runQuery') }}
|
||||
</div>
|
||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
||||
{{ $t('message.killQuery') }}
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
{{ $t('word.format') }}
|
||||
</div>
|
||||
|
@ -25,6 +28,9 @@
|
|||
<div class="mb-4">
|
||||
<code>F5</code>
|
||||
</div>
|
||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
||||
<code>CTRL</code> + <code>K</code>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<code>CTRL</code> + <code>B</code>
|
||||
</div>
|
||||
|
@ -47,7 +53,10 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'WorkspaceTabQueryEmptyState'
|
||||
name: 'WorkspaceTabQueryEmptyState',
|
||||
props: {
|
||||
customizations: Object
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -251,7 +251,8 @@ module.exports = {
|
|||
killProcess: 'Kill process',
|
||||
closeTab: 'Close tab',
|
||||
goToDownloadPage: 'Go to download page',
|
||||
readOnlyMode: 'Read-only mode'
|
||||
readOnlyMode: 'Read-only mode',
|
||||
killQuery: 'Kill query'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
|
|
@ -46,6 +46,10 @@ export default class {
|
|||
return ipcRenderer.invoke('kill-process', params);
|
||||
}
|
||||
|
||||
static killTabQuery (params) {
|
||||
return ipcRenderer.invoke('kill-tab-query', params);
|
||||
}
|
||||
|
||||
static useSchema (params) {
|
||||
return ipcRenderer.invoke('use-schema', params);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,34 @@ option:checked {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cancellable {
|
||||
color: transparent !important;
|
||||
min-height: 0.8rem;
|
||||
position: relative;
|
||||
|
||||
> .mdi,
|
||||
> .span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "\2715";
|
||||
color: $light-color;
|
||||
font-weight: 700;
|
||||
top: 36%;
|
||||
display: block;
|
||||
height: 0.8rem;
|
||||
left: 50%;
|
||||
margin-left: -0.4rem;
|
||||
margin-top: -0.4rem;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 0.8rem;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-tabs {
|
||||
align-content: baseline;
|
||||
|
||||
|
|
Loading…
Reference in New Issue