mirror of
				https://github.com/Fabio286/antares.git
				synced 2025-06-05 21:59:22 +02:00 
			
		
		
		
	feat(MySQL): ability to cancel queries
This commit is contained in:
		| @@ -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,16 +23,29 @@ | ||||
|          <div ref="resizer" class="query-area-resizer" /> | ||||
|          <div class="workspace-query-runner-footer"> | ||||
|             <div class="workspace-query-buttons"> | ||||
|                <button | ||||
|                   class="btn btn-primary btn-sm" | ||||
|                   :class="{'loading':isQuering}" | ||||
|                   :disabled="!query" | ||||
|                   title="F5" | ||||
|                   @click="runQuery(query)" | ||||
|                > | ||||
|                   <i class="mdi mdi-24px mdi-play pr-1" /> | ||||
|                   <span>{{ $t('word.run') }}</span> | ||||
|                </button> | ||||
|                <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" | ||||
|                      title="F5" | ||||
|                      @click="runQuery(query)" | ||||
|                   > | ||||
|                      <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; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user