From 2e49d86677a57fae38bf904f0561be8b8dc37bf8 Mon Sep 17 00:00:00 2001 From: Fabio Date: Tue, 27 Oct 2020 16:41:00 +0100 Subject: [PATCH] refactor(core): improved how application gets query fields and keys --- src/main/ipc-handlers/database.js | 2 +- src/main/ipc-handlers/tables.js | 2 +- src/main/libs/AntaresCore.js | 5 +- src/main/libs/clients/MySQLClient.js | 99 +++++++++++-- src/renderer/components/ModalNewTableRow.vue | 11 +- src/renderer/components/WorkspacePropsTab.vue | 17 +-- src/renderer/components/WorkspaceQueryTab.vue | 132 ++---------------- .../components/WorkspaceQueryTable.vue | 16 +-- src/renderer/components/WorkspaceTableTab.vue | 50 ++----- 9 files changed, 129 insertions(+), 205 deletions(-) diff --git a/src/main/ipc-handlers/database.js b/src/main/ipc-handlers/database.js index dda7f265..9f5021a4 100644 --- a/src/main/ipc-handlers/database.js +++ b/src/main/ipc-handlers/database.js @@ -99,7 +99,7 @@ export default connections => { if (!query) return; try { - const result = await connections[uid].raw(query, true); + const result = await connections[uid].raw(query, { nest: true, details: true }); return { status: 'success', response: result }; } diff --git a/src/main/ipc-handlers/tables.js b/src/main/ipc-handlers/tables.js index d3315867..5ddbeef0 100644 --- a/src/main/ipc-handlers/tables.js +++ b/src/main/ipc-handlers/tables.js @@ -21,7 +21,7 @@ export default (connections) => { .schema(schema) .from(table) .limit(1000) - .run(); + .run({ details: true }); return { status: 'success', response: result }; } diff --git a/src/main/libs/AntaresCore.js b/src/main/libs/AntaresCore.js index c3c0d06f..4d9b1295 100644 --- a/src/main/libs/AntaresCore.js +++ b/src/main/libs/AntaresCore.js @@ -130,12 +130,13 @@ export class AntaresCore { } /** + * @param {Object} args * @returns {Promise} * @memberof AntaresCore */ - async run () { + async run (args) { const rawQuery = this.getSQL(); this._resetQuery(); - return this.raw(rawQuery); + return this.raw(rawQuery, args); } } diff --git a/src/main/libs/clients/MySQLClient.js b/src/main/libs/clients/MySQLClient.js index f042effd..92a30722 100644 --- a/src/main/libs/clients/MySQLClient.js +++ b/src/main/libs/clients/MySQLClient.js @@ -329,22 +329,35 @@ export class MySQLClient extends AntaresCore { /** * @param {string} sql raw SQL query - * @param {boolean} [nest] + * @param {object} args + * @param {boolean} args.nest + * @param {boolean} args.details * @returns {Promise} * @memberof MySQLClient */ - async raw (sql, nest) { - const nestTables = nest ? '.' : false; + async raw (sql, args) { + args = { + nest: false, + details: false, + ...args + }; + const nestTables = args.nest ? '.' : false; const resultsArr = []; + let paramsArr = []; + let selectedFields = []; const queries = sql.split(';'); if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder for (const query of queries) { if (!query) continue; + let fieldsArr = []; + let keysArr = []; + + const { rows, report, fields, keys } = await new Promise((resolve, reject) => { + this._connection.query({ sql: query, nestTables }, async (err, response, fields) => { + const queryResult = response; - const { rows, report, fields } = await new Promise((resolve, reject) => { - this._connection.query({ sql: query, nestTables }, (err, response, fields) => { if (err) reject(err); else { @@ -359,15 +372,83 @@ export class MySQLClient extends AntaresCore { }; }) : []; + // TODO: move here fields and keys requests + if (args.details) { + let cachedTable; + + if (remappedFields.length) { + selectedFields = remappedFields.map(field => { + return { + name: field.orgName || field.name, + table: field.orgTable || field.table + }; + }); + + paramsArr = remappedFields.map(field => { + if (field.table) cachedTable = field.table;// Needed for some queries on information_schema + return { + table: field.orgTable || cachedTable, + schema: field.schema || 'INFORMATION_SCHEMA' + }; + }).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i); + + for (const paramObj of paramsArr) { + try { // Table data + const response = await this.getTableColumns(paramObj); + + let detailedFields = response.length ? selectedFields.map(selField => { + return response.find(field => field.name === selField.name && field.table === selField.table); + }).filter(el => !!el) : []; + + if (selectedFields.length) { + detailedFields = detailedFields.map(field => { + const aliasObj = remappedFields.find(resField => resField.orgName === field.name); + return { + ...field, + alias: aliasObj.name || field.name, + tableAlias: aliasObj.table || field.table + }; + }); + } + + if (!detailedFields.length) { + detailedFields = remappedFields.map(field => { + return { + ...field, + alias: field.name, + tableAlias: field.table + }; + }); + } + + fieldsArr = fieldsArr ? [...fieldsArr, ...detailedFields] : detailedFields; + } + catch (err) { + reject(err); + } + + try { // Key usage (foreign keys) + const response = await this.getKeyUsage(paramObj); + keysArr = keysArr ? [...keysArr, ...response] : response; + } + catch (err) { + reject(err); + } + } + } + } + resolve({ - rows: Array.isArray(response) ? response : false, - report: !Array.isArray(response) ? response : false, - fields: remappedFields + rows: Array.isArray(queryResult) ? queryResult : false, + report: !Array.isArray(queryResult) ? queryResult : false, + fields: fieldsArr.length ? fieldsArr : remappedFields, + keys: keysArr }); } }); }); - resultsArr.push({ rows, report, fields }); + + resultsArr.push({ rows, report, fields, keys }); } return resultsArr.length === 1 ? resultsArr[0] : resultsArr; diff --git a/src/renderer/components/ModalNewTableRow.vue b/src/renderer/components/ModalNewTableRow.vue index 2d716030..f0cbc611 100644 --- a/src/renderer/components/ModalNewTableRow.vue +++ b/src/renderer/components/ModalNewTableRow.vue @@ -124,8 +124,9 @@ export default { } }, props: { - connection: Object, - tabUid: [String, Number] + tabUid: [String, Number], + fields: Array, + keyUsage: Array }, data () { return { @@ -146,12 +147,6 @@ export default { }, foreignKeys () { return this.keyUsage.map(key => key.column); - }, - fields () { - return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).fields[0] : []; - }, - keyUsage () { - return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).keyUsage[0] : []; } }, watch: { diff --git a/src/renderer/components/WorkspacePropsTab.vue b/src/renderer/components/WorkspacePropsTab.vue index aff977d7..a403edd6 100644 --- a/src/renderer/components/WorkspacePropsTab.vue +++ b/src/renderer/components/WorkspacePropsTab.vue @@ -17,28 +17,19 @@
- - - - diff --git a/src/renderer/components/WorkspaceQueryTab.vue b/src/renderer/components/WorkspaceQueryTab.vue index 5edf7ee6..36be9cdc 100644 --- a/src/renderer/components/WorkspaceQueryTab.vue +++ b/src/renderer/components/WorkspaceQueryTab.vue @@ -16,10 +16,10 @@
-
+
{{ $t('word.results') }}: {{ resultsCount }}
-
+
{{ $t('message.affectedRows') }}: {{ affectedCount }}
import Database from '@/ipc-api/Database'; -import Tables from '@/ipc-api/Tables'; import QueryEditor from '@/components/QueryEditor'; import WorkspaceQueryTable from '@/components/WorkspaceQueryTable'; import { mapGetters, mapActions } from 'vuex'; @@ -74,8 +73,8 @@ export default { lastQuery: '', isQuering: false, results: [], - resultsCount: false, - affectedCount: false + resultsCount: 0, + affectedCount: 0 }; }, computed: { @@ -94,25 +93,8 @@ export default { }, methods: { ...mapActions({ - addNotification: 'notifications/addNotification', - setTabFields: 'workspaces/setTabFields', - setTabKeyUsage: 'workspaces/setTabKeyUsage' + addNotification: 'notifications/addNotification' }), - getResultParams (index) { - const resultsWithRows = this.results.filter(result => result.rows); - let cachedTable; - - if (resultsWithRows[index] && resultsWithRows[index].fields && resultsWithRows[index].fields.length) { - return resultsWithRows[index].fields.map(field => { - if (field.table) cachedTable = field.table;// Needed for some queries on information_schema - return { - table: field.orgTable || cachedTable, - schema: field.schema || 'INFORMATION_SCHEMA' - }; - }).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i); - } - return []; - }, async runQuery (query) { if (!query || this.isQuering) return; this.isQuering = true; @@ -129,103 +111,8 @@ export default { if (status === 'success') { this.results = Array.isArray(response) ? response : [response]; - - let selectedFields = []; - const fieldsArr = []; - const keysArr = []; - let qI = 0;// queries index - - for (const result of this.results) { // cycle queries - if (result.rows) { // if is a select - const paramsArr = this.getResultParams(qI); - - selectedFields = result.fields.map(field => { - return { - name: field.orgName || field.name, - table: field.orgTable || field.table - }; - }); - - this.resultsCount += result.rows.length; - - for (const paramObj of paramsArr) { - try { // Table data - const params = { - uid: this.connection.uid, - ...paramObj - }; - - const { status, response } = await Tables.getTableColumns(params); - - if (status === 'success') { - let fields = response.length ? selectedFields.map(selField => { - return response.find(field => field.name === selField.name && field.table === selField.table); - }).filter(el => !!el) : []; - - if (selectedFields.length) { - fields = fields.map(field => { - const aliasObj = result.fields.find(resField => resField.orgName === field.name); - return { - ...field, - alias: aliasObj.name || field.name, - tableAlias: aliasObj.table || field.table - }; - }); - } - - if (!fields.length) { - fields = result.fields.map(field => { - return { - ...field, - alias: field.name, - tableAlias: field.table - }; - }); - } - - fieldsArr[qI] = fieldsArr[qI] ? [...fieldsArr[qI], ...fields] : fields; - } - else - this.addNotification({ status: 'error', message: response }); - } - catch (err) { - this.addNotification({ status: 'error', message: err.stack }); - } - - try { // Key usage (foreign keys) - const params = { - uid: this.connection.uid, - ...paramObj - }; - - const { status, response } = await Tables.getKeyUsage(params); - if (status === 'success') - keysArr[qI] = keysArr[qI] ? [...keysArr[qI], ...response] : response; - else - this.addNotification({ status: 'error', message: response }); - } - catch (err) { - this.addNotification({ status: 'error', message: err.stack }); - } - } - } - else if (result.report) { // if is a query without output - this.affectedCount += result.report.affectedRows; - } - - qI++; - } - - this.setTabFields({ - cUid: this.connection.uid, - tUid: this.tabUid, - fields: fieldsArr - }); - this.setTabKeyUsage({ - cUid: this.connection.uid, - tUid: this.tabUid, - keyUsage: keysArr - }); + this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0); + this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0); } else this.addNotification({ status: 'error', message: response }); @@ -242,9 +129,8 @@ export default { }, clearTabData () { this.results = []; - this.resultsCount = false; - this.affectedCount = false; - this.setTabFields({ cUid: this.connection.uid, tUid: this.tabUid, fields: [] }); + this.resultsCount = 0; + this.affectedCount = 0; }, onKey (e) { e.stopPropagation(); diff --git a/src/renderer/components/WorkspaceQueryTable.vue b/src/renderer/components/WorkspaceQueryTable.vue index 2fa53f12..fd36a26e 100644 --- a/src/renderer/components/WorkspaceQueryTable.vue +++ b/src/renderer/components/WorkspaceQueryTable.vue @@ -115,7 +115,6 @@ export default { }, computed: { ...mapGetters({ - getWorkspaceTab: 'workspaces/getWorkspaceTab', getWorkspace: 'workspaces/getWorkspace' }), workspaceSchema () { @@ -142,14 +141,11 @@ export default { resultsWithRows () { return this.results.filter(result => result.rows); }, - tabProperties () { - return this.getWorkspaceTab(this.tabUid); - }, fields () { - return this.tabProperties && this.tabProperties.fields[this.resultsetIndex] ? this.tabProperties.fields[this.resultsetIndex] : []; + return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].fields : []; }, keyUsage () { - return this.tabProperties && this.tabProperties.keyUsage[this.resultsetIndex] ? this.tabProperties.keyUsage[this.resultsetIndex] : []; + return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].keys : []; } }, watch: { @@ -212,7 +208,7 @@ export default { }, getTable (index) { if (this.resultsWithRows[index] && this.resultsWithRows[index].fields && this.resultsWithRows[index].fields.length) - return this.resultsWithRows[index].fields[0].orgTable; + return this.resultsWithRows[index].fields[0].table; return ''; }, getSchema (index) { @@ -270,7 +266,11 @@ export default { if (!this.primaryField) this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') }); else { - const rowIDs = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => row[this.primaryField.name]); + const rowIDs = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => + row[this.primaryField.name] || + row[`${this.primaryField.table}.${this.primaryField.name}`] || + row[`${this.primaryField.tableAlias}.${this.primaryField.name}`] + ); const params = { primary: this.primaryField.name, schema: this.getSchema(this.resultsetIndex), diff --git a/src/renderer/components/WorkspaceTableTab.vue b/src/renderer/components/WorkspaceTableTab.vue index 748408dc..f416a161 100644 --- a/src/renderer/components/WorkspaceTableTab.vue +++ b/src/renderer/components/WorkspaceTableTab.vue @@ -64,6 +64,8 @@