1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

refactor(core): improved how application gets query fields and keys

This commit is contained in:
2020-10-27 16:41:00 +01:00
parent c393f86947
commit 2e49d86677
9 changed files with 129 additions and 205 deletions

View File

@ -99,7 +99,7 @@ export default connections => {
if (!query) return; if (!query) return;
try { 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 }; return { status: 'success', response: result };
} }

View File

@ -21,7 +21,7 @@ export default (connections) => {
.schema(schema) .schema(schema)
.from(table) .from(table)
.limit(1000) .limit(1000)
.run(); .run({ details: true });
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@ -130,12 +130,13 @@ export class AntaresCore {
} }
/** /**
* @param {Object} args
* @returns {Promise} * @returns {Promise}
* @memberof AntaresCore * @memberof AntaresCore
*/ */
async run () { async run (args) {
const rawQuery = this.getSQL(); const rawQuery = this.getSQL();
this._resetQuery(); this._resetQuery();
return this.raw(rawQuery); return this.raw(rawQuery, args);
} }
} }

View File

@ -329,22 +329,35 @@ export class MySQLClient extends AntaresCore {
/** /**
* @param {string} sql raw SQL query * @param {string} sql raw SQL query
* @param {boolean} [nest] * @param {object} args
* @param {boolean} args.nest
* @param {boolean} args.details
* @returns {Promise} * @returns {Promise}
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async raw (sql, nest) { async raw (sql, args) {
const nestTables = nest ? '.' : false; args = {
nest: false,
details: false,
...args
};
const nestTables = args.nest ? '.' : false;
const resultsArr = []; const resultsArr = [];
let paramsArr = [];
let selectedFields = [];
const queries = sql.split(';'); const queries = sql.split(';');
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
for (const query of queries) { for (const query of queries) {
if (!query) continue; 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) if (err)
reject(err); reject(err);
else { 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({ resolve({
rows: Array.isArray(response) ? response : false, rows: Array.isArray(queryResult) ? queryResult : false,
report: !Array.isArray(response) ? response : false, report: !Array.isArray(queryResult) ? queryResult : false,
fields: remappedFields 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; return resultsArr.length === 1 ? resultsArr[0] : resultsArr;

View File

@ -124,8 +124,9 @@ export default {
} }
}, },
props: { props: {
connection: Object, tabUid: [String, Number],
tabUid: [String, Number] fields: Array,
keyUsage: Array
}, },
data () { data () {
return { return {
@ -146,12 +147,6 @@ export default {
}, },
foreignKeys () { foreignKeys () {
return this.keyUsage.map(key => key.column); 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: { watch: {

View File

@ -17,28 +17,19 @@
<div class="divider-vert py-3" /> <div class="divider-vert py-3" />
<button <button class="btn btn-dark btn-sm" :title="$t('message.addNewField')">
class="btn btn-dark btn-sm"
:title="$t('message.addNewField')"
>
<span>{{ $t('word.add') }}</span> <span>{{ $t('word.add') }}</span>
<i class="mdi mdi-24px mdi-playlist-plus ml-1" /> <i class="mdi mdi-24px mdi-playlist-plus ml-1" />
</button> </button>
<button <button class="btn btn-dark btn-sm">
class="btn btn-dark btn-sm"
>
<span>{{ $t('word.indexes') }}</span> <span>{{ $t('word.indexes') }}</span>
<i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" /> <i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" />
</button> </button>
<button <button class="btn btn-dark btn-sm">
class="btn btn-dark btn-sm"
>
<span>{{ $t('word.foreignKeys') }}</span> <span>{{ $t('word.foreignKeys') }}</span>
<i class="mdi mdi-24px mdi-key-link ml-1" /> <i class="mdi mdi-24px mdi-key-link ml-1" />
</button> </button>
<button <button class="btn btn-dark btn-sm">
class="btn btn-dark btn-sm"
>
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
<i class="mdi mdi-24px mdi-cogs ml-1" /> <i class="mdi mdi-24px mdi-cogs ml-1" />
</button> </button>

View File

@ -16,10 +16,10 @@
</button> </button>
</div> </div>
<div class="workspace-query-info"> <div class="workspace-query-info">
<div v-if="resultsCount !== false"> <div v-if="resultsCount">
{{ $t('word.results') }}: <b>{{ resultsCount }}</b> {{ $t('word.results') }}: <b>{{ resultsCount }}</b>
</div> </div>
<div v-if="affectedCount !== false"> <div v-if="affectedCount">
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b> {{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
</div> </div>
<div <div
@ -50,7 +50,6 @@
<script> <script>
import Database from '@/ipc-api/Database'; import Database from '@/ipc-api/Database';
import Tables from '@/ipc-api/Tables';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable'; import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
@ -74,8 +73,8 @@ export default {
lastQuery: '', lastQuery: '',
isQuering: false, isQuering: false,
results: [], results: [],
resultsCount: false, resultsCount: 0,
affectedCount: false affectedCount: 0
}; };
}, },
computed: { computed: {
@ -94,25 +93,8 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification'
setTabFields: 'workspaces/setTabFields',
setTabKeyUsage: 'workspaces/setTabKeyUsage'
}), }),
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) { async runQuery (query) {
if (!query || this.isQuering) return; if (!query || this.isQuering) return;
this.isQuering = true; this.isQuering = true;
@ -129,103 +111,8 @@ export default {
if (status === 'success') { if (status === 'success') {
this.results = Array.isArray(response) ? response : [response]; this.results = Array.isArray(response) ? response : [response];
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
let selectedFields = []; this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
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
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@ -242,9 +129,8 @@ export default {
}, },
clearTabData () { clearTabData () {
this.results = []; this.results = [];
this.resultsCount = false; this.resultsCount = 0;
this.affectedCount = false; this.affectedCount = 0;
this.setTabFields({ cUid: this.connection.uid, tUid: this.tabUid, fields: [] });
}, },
onKey (e) { onKey (e) {
e.stopPropagation(); e.stopPropagation();

View File

@ -115,7 +115,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
getWorkspaceTab: 'workspaces/getWorkspaceTab',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspaceSchema () { workspaceSchema () {
@ -142,14 +141,11 @@ export default {
resultsWithRows () { resultsWithRows () {
return this.results.filter(result => result.rows); return this.results.filter(result => result.rows);
}, },
tabProperties () {
return this.getWorkspaceTab(this.tabUid);
},
fields () { fields () {
return this.tabProperties && this.tabProperties.fields[this.resultsetIndex] ? this.tabProperties.fields[this.resultsetIndex] : []; return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].fields : [];
}, },
keyUsage () { keyUsage () {
return this.tabProperties && this.tabProperties.keyUsage[this.resultsetIndex] ? this.tabProperties.keyUsage[this.resultsetIndex] : []; return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].keys : [];
} }
}, },
watch: { watch: {
@ -212,7 +208,7 @@ export default {
}, },
getTable (index) { getTable (index) {
if (this.resultsWithRows[index] && this.resultsWithRows[index].fields && this.resultsWithRows[index].fields.length) 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 ''; return '';
}, },
getSchema (index) { getSchema (index) {
@ -270,7 +266,11 @@ export default {
if (!this.primaryField) if (!this.primaryField)
this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') }); this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') });
else { 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 = { const params = {
primary: this.primaryField.name, primary: this.primaryField.name,
schema: this.getSchema(this.resultsetIndex), schema: this.getSchema(this.resultsetIndex),

View File

@ -64,6 +64,8 @@
</div> </div>
<ModalNewTableRow <ModalNewTableRow
v-if="isAddModal" v-if="isAddModal"
:fields="fields"
:key-usage="keyUsage"
:tab-uid="tabUid" :tab-uid="tabUid"
@hide="hideAddModal" @hide="hideAddModal"
@reload="reloadTable" @reload="reloadTable"
@ -94,8 +96,6 @@ export default {
tabUid: 'data', tabUid: 'data',
isQuering: false, isQuering: false,
results: [], results: [],
fields: [],
keyUsage: [],
lastTable: null, lastTable: null,
isAddModal: false, isAddModal: false,
autorefreshTimer: 0, autorefreshTimer: 0,
@ -111,6 +111,12 @@ export default {
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'data'; return this.workspace.selected_tab === 'data';
},
fields () {
return this.results.length ? this.results[0].fields : [];
},
keyUsage () {
return this.results.length ? this.results[0].keys : [];
} }
}, },
watch: { watch: {
@ -137,21 +143,15 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification'
setTabFields: 'workspaces/setTabFields',
setTabKeyUsage: 'workspaces/setTabKeyUsage'
}), }),
async getTableData () { async getTableData () {
if (!this.table) return; if (!this.table) return;
this.isQuering = true; this.isQuering = true;
const fieldsArr = [];
const keysArr = [];
// if table changes clear cached values // if table changes clear cached values
if (this.lastTable !== this.table) { if (this.lastTable !== this.table)
this.results = []; this.results = [];
this.setTabFields({ cUid: this.connection.uid, tUid: this.tabUid, fields: [] });
}
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
@ -159,33 +159,6 @@ export default {
table: this.workspace.breadcrumbs.table table: this.workspace.breadcrumbs.table
}; };
try { // Columns data
const { status, response } = await Tables.getTableColumns(params);
if (status === 'success') {
this.fields = response;// Needed to add new rows
fieldsArr.push(response);
}
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
try { // Key usage (foreign keys)
const { status, response } = await Tables.getKeyUsage(params);
if (status === 'success') {
this.keyUsage = response;// Needed to add new rows
keysArr.push(response);
}
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
try { // Table data try { // Table data
const { status, response } = await Tables.getTableData(params); const { status, response } = await Tables.getTableData(params);
@ -198,9 +171,6 @@ export default {
this.addNotification({ status: 'error', message: err.stack }); this.addNotification({ status: 'error', message: err.stack });
} }
this.setTabFields({ cUid: this.connection.uid, tUid: this.tabUid, fields: fieldsArr });
this.setTabKeyUsage({ cUid: this.connection.uid, tUid: this.tabUid, keyUsage: keysArr });
this.isQuering = false; this.isQuering = false;
}, },
getTable () { getTable () {