mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
04fcaf2f6e | |||
8b914446b0 | |||
a11bac504c | |||
b9ed8dd610 | |||
1cf6485896 | |||
4bc9bbfb34 | |||
4923128236 | |||
8ff6e70145 |
7
.versionrc.json
Normal file
7
.versionrc.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"types": [
|
||||
{"type":"feat","section":"Features"},
|
||||
{"type":"perf","section":"Improvements"},
|
||||
{"type":"fix","section":"Bug Fixes"}
|
||||
]
|
||||
}
|
20
CHANGELOG.md
20
CHANGELOG.md
@@ -2,6 +2,26 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.0.15](https://github.com/Fabio286/antares/compare/v0.0.14...v0.0.15) (2021-01-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* functions and schedulers in query suggestions ([8ff6e70](https://github.com/Fabio286/antares/commit/8ff6e70145ed2a207ae8b23a2c688258382a5d74))
|
||||
* loading animation in properties tabs ([1cf6485](https://github.com/Fabio286/antares/commit/1cf64858964f4894913db42f7c268013bb06e40b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error retriving dato of some schedulers ([b9ed8dd](https://github.com/Fabio286/antares/commit/b9ed8dd610e3be1489e01cf53f7d632cb1bd6ac5))
|
||||
* unable to call stored routines from query tabs ([4923128](https://github.com/Fabio286/antares/commit/4923128236131482ca948ae8052c294bd9269ed0))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* better fields type detection ([4bc9bbf](https://github.com/Fabio286/antares/commit/4bc9bbfb34ebdc51061f718cdf9cbca8507fa0f4))
|
||||
* big performance improvement in database structure loading ([a11bac5](https://github.com/Fabio286/antares/commit/a11bac504cd4ee865ea6c614a15ee809dc38202e))
|
||||
|
||||
### [0.0.14](https://github.com/Fabio286/antares/compare/v0.0.13...v0.0.14) (2021-01-16)
|
||||
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.0.14",
|
||||
"version": "0.0.15",
|
||||
"description": "A cross-platform easy to use SQL client.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Fabio286/antares.git",
|
||||
@@ -67,8 +67,7 @@
|
||||
"vue-i18n": "^8.22.2",
|
||||
"vue-the-mask": "^0.11.1",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.6.0",
|
||||
"vuex-persist": "^3.1.3"
|
||||
"vuex": "^3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.1.0",
|
||||
|
@@ -91,7 +91,7 @@ module.exports = [
|
||||
},
|
||||
{
|
||||
name: 'TINYTEXT',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
@@ -119,7 +119,7 @@ module.exports = [
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
@@ -279,7 +279,7 @@ module.exports = [
|
||||
types: [
|
||||
{
|
||||
name: 'UNKNOWN',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
|
@@ -7,6 +7,6 @@ export const DATE = ['DATE'];
|
||||
export const TIME = ['TIME'];
|
||||
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
||||
|
||||
export const BLOB = ['BLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
||||
export const BLOB = ['BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
||||
|
||||
export const BIT = ['BIT'];
|
||||
|
@@ -46,7 +46,7 @@ export default connections => {
|
||||
try {
|
||||
await connection.connect();
|
||||
|
||||
const structure = await connection.getStructure();
|
||||
const structure = await connection.getStructure(new Set());
|
||||
|
||||
connections[conn.uid] = connection;
|
||||
|
||||
|
@@ -50,9 +50,9 @@ export default connections => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-structure', async (event, uid) => {
|
||||
ipcMain.handle('get-structure', async (event, params) => {
|
||||
try {
|
||||
const structure = await connections[uid].getStructure();
|
||||
const structure = await connections[params.uid].getStructure(params.schemas);
|
||||
|
||||
return { status: 'success', response: structure };
|
||||
}
|
||||
|
@@ -1,8 +1,97 @@
|
||||
'use strict';
|
||||
import mysql from 'mysql';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/mysql';
|
||||
|
||||
export class MySQLClient extends AntaresCore {
|
||||
constructor (args) {
|
||||
super(args);
|
||||
|
||||
this.types = {
|
||||
0: 'DECIMAL',
|
||||
1: 'TINYINT',
|
||||
2: 'SMALLINT',
|
||||
3: 'INT',
|
||||
4: 'FLOAT',
|
||||
5: 'DOUBLE',
|
||||
6: 'NULL',
|
||||
7: 'TIMESTAMP',
|
||||
8: 'BIGINT',
|
||||
9: 'MEDIUMINT',
|
||||
10: 'DATE',
|
||||
11: 'TIME',
|
||||
12: 'DATETIME',
|
||||
13: 'YEAR',
|
||||
14: 'NEWDATE',
|
||||
15: 'VARCHAR',
|
||||
16: 'BIT',
|
||||
17: 'TIMESTAMP2',
|
||||
18: 'DATETIME2',
|
||||
19: 'TIME2',
|
||||
245: 'JSON',
|
||||
246: 'NEWDECIMAL',
|
||||
247: 'ENUM',
|
||||
248: 'SET',
|
||||
249: 'TINY_BLOB',
|
||||
250: 'MEDIUM_BLOB',
|
||||
251: 'LONG_BLOB',
|
||||
252: 'BLOB',
|
||||
253: 'VARCHAR',
|
||||
254: 'CHAR',
|
||||
255: 'GEOMETRY'
|
||||
};
|
||||
}
|
||||
|
||||
_getType (field) {
|
||||
let name = this.types[field.type];
|
||||
let length = field.length;
|
||||
|
||||
if (['DATE', 'TIME', 'YEAR', 'DATETIME'].includes(name))
|
||||
length = field.decimals;
|
||||
|
||||
if (name === 'CHAR' && field.charsetNr === 63)// if binary
|
||||
name = 'BINARY';
|
||||
|
||||
if (name === 'VARCHAR' && field.charsetNr === 63)// if binary
|
||||
name = 'VARBINARY';
|
||||
|
||||
if (name === 'BLOB') {
|
||||
switch (length) {
|
||||
case 765:
|
||||
name = 'TYNITEXT';
|
||||
break;
|
||||
case 196605:
|
||||
name = 'TEXT';
|
||||
break;
|
||||
case 50331645:
|
||||
name = 'MEDIUMTEXT';
|
||||
break;
|
||||
case 4294967295:
|
||||
name = field.charsetNr === 63 ? 'LONGBLOB' : 'LONGTEXT';
|
||||
break;
|
||||
case 255:
|
||||
name = 'TINYBLOB';
|
||||
break;
|
||||
case 65535:
|
||||
name = 'BLOB';
|
||||
break;
|
||||
case 16777215:
|
||||
name = 'MEDIUMBLOB';
|
||||
break;
|
||||
default:
|
||||
name = field.charsetNr === 63 ? 'BLOB' : 'TEXT';
|
||||
}
|
||||
}
|
||||
|
||||
return { name, length };
|
||||
}
|
||||
|
||||
_getTypeInfo (type) {
|
||||
return dataTypes
|
||||
.reduce((acc, group) => [...acc, ...group.types], [])
|
||||
.filter(_type => _type.name === type.toUpperCase())[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
@@ -31,10 +120,11 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} schemas list
|
||||
* @returns {Array.<Object>} databases scructure
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async getStructure () {
|
||||
async getStructure (schemas) {
|
||||
const { rows: databases } = await this.raw('SHOW DATABASES');
|
||||
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
|
||||
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
|
||||
@@ -44,6 +134,8 @@ export class MySQLClient extends AntaresCore {
|
||||
const triggersArr = [];
|
||||
|
||||
for (const db of databases) {
|
||||
if (!schemas.has(db.Database)) continue;
|
||||
|
||||
let { rows: tables } = await this.raw(`SHOW TABLE STATUS FROM \`${db.Database}\``);
|
||||
if (tables.length) {
|
||||
tables = tables.map(table => {
|
||||
@@ -64,109 +156,121 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
return databases.map(db => {
|
||||
// TABLES
|
||||
const remappedTables = tablesArr.filter(table => table.Db === db.Database).map(table => {
|
||||
let tableType;
|
||||
switch (table.Comment) {
|
||||
case 'VIEW':
|
||||
tableType = 'view';
|
||||
break;
|
||||
default:
|
||||
tableType = 'table';
|
||||
break;
|
||||
}
|
||||
if (schemas.has(db.Database)) {
|
||||
// TABLES
|
||||
const remappedTables = tablesArr.filter(table => table.Db === db.Database).map(table => {
|
||||
let tableType;
|
||||
switch (table.Comment) {
|
||||
case 'VIEW':
|
||||
tableType = 'view';
|
||||
break;
|
||||
default:
|
||||
tableType = 'table';
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
name: table.Name,
|
||||
type: tableType,
|
||||
rows: table.Rows,
|
||||
created: table.Create_time,
|
||||
updated: table.Update_time,
|
||||
engine: table.Engine,
|
||||
comment: table.Comment,
|
||||
size: table.Data_length + table.Index_length,
|
||||
autoIncrement: table.Auto_increment,
|
||||
collation: table.Collation
|
||||
};
|
||||
});
|
||||
|
||||
// PROCEDURES
|
||||
const remappedProcedures = procedures.filter(procedure => procedure.Db === db.Database).map(procedure => {
|
||||
return {
|
||||
name: procedure.Name,
|
||||
type: procedure.Type,
|
||||
definer: procedure.Definer,
|
||||
created: procedure.Created,
|
||||
updated: procedure.Modified,
|
||||
comment: procedure.Comment,
|
||||
charset: procedure.character_set_client,
|
||||
security: procedure.Security_type
|
||||
};
|
||||
});
|
||||
|
||||
// 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 {
|
||||
name: scheduler.EVENT_NAME,
|
||||
definition: scheduler.EVENT_DEFINITION,
|
||||
type: scheduler.EVENT_TYPE,
|
||||
definer: scheduler.DEFINER,
|
||||
body: scheduler.EVENT_BODY,
|
||||
starts: scheduler.STARTS,
|
||||
ends: scheduler.ENDS,
|
||||
status: scheduler.STATUS,
|
||||
executeAt: scheduler.EXECUTE_AT,
|
||||
intervalField: scheduler.INTERVAL_FIELD,
|
||||
intervalValue: scheduler.INTERVAL_VALUE,
|
||||
onCompletion: scheduler.ON_COMPLETION,
|
||||
originator: scheduler.ORIGINATOR,
|
||||
sqlMode: scheduler.SQL_MODE,
|
||||
created: scheduler.CREATED,
|
||||
updated: scheduler.LAST_ALTERED,
|
||||
lastExecuted: scheduler.LAST_EXECUTED,
|
||||
comment: scheduler.EVENT_COMMENT,
|
||||
charset: scheduler.CHARACTER_SET_CLIENT,
|
||||
timezone: scheduler.TIME_ZONE
|
||||
};
|
||||
});
|
||||
|
||||
// TRIGGERS
|
||||
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.Database).map(trigger => {
|
||||
return {
|
||||
name: trigger.Trigger,
|
||||
statement: trigger.Statement,
|
||||
timing: trigger.Timing,
|
||||
definer: trigger.Definer,
|
||||
event: trigger.Event,
|
||||
table: trigger.Table,
|
||||
sqlMode: trigger.sql_mode,
|
||||
created: trigger.Created,
|
||||
charset: trigger.character_set_client
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: table.Name,
|
||||
type: tableType,
|
||||
rows: table.Rows,
|
||||
created: table.Create_time,
|
||||
updated: table.Update_time,
|
||||
engine: table.Engine,
|
||||
comment: table.Comment,
|
||||
size: table.Data_length + table.Index_length,
|
||||
autoIncrement: table.Auto_increment,
|
||||
collation: table.Collation
|
||||
name: db.Database,
|
||||
tables: remappedTables,
|
||||
functions: remappedFunctions,
|
||||
procedures: remappedProcedures,
|
||||
triggers: remappedTriggers,
|
||||
schedulers: remappedSchedulers
|
||||
};
|
||||
});
|
||||
|
||||
// PROCEDURES
|
||||
const remappedProcedures = procedures.filter(procedure => procedure.Db === db.Database).map(procedure => {
|
||||
}
|
||||
else {
|
||||
return {
|
||||
name: procedure.Name,
|
||||
type: procedure.Type,
|
||||
definer: procedure.Definer,
|
||||
created: procedure.Created,
|
||||
updated: procedure.Modified,
|
||||
comment: procedure.Comment,
|
||||
charset: procedure.character_set_client,
|
||||
security: procedure.Security_type
|
||||
name: db.Database,
|
||||
tables: [],
|
||||
functions: [],
|
||||
procedures: [],
|
||||
triggers: [],
|
||||
schedulers: []
|
||||
};
|
||||
});
|
||||
|
||||
// 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 {
|
||||
name: scheduler.EVENT_NAME,
|
||||
definition: scheduler.EVENT_DEFINITION,
|
||||
type: scheduler.EVENT_TYPE,
|
||||
definer: scheduler.DEFINER,
|
||||
body: scheduler.EVENT_BODY,
|
||||
starts: scheduler.STARTS,
|
||||
ends: scheduler.ENDS,
|
||||
status: scheduler.STATUS,
|
||||
executeAt: scheduler.EXECUTE_AT,
|
||||
intervalField: scheduler.INTERVAL_FIELD,
|
||||
intervalValue: scheduler.INTERVAL_VALUE,
|
||||
onCompletion: scheduler.ON_COMPLETION,
|
||||
originator: scheduler.ORIGINATOR,
|
||||
sqlMode: scheduler.SQL_MODE,
|
||||
created: scheduler.CREATED,
|
||||
updated: scheduler.LAST_ALTERED,
|
||||
lastExecuted: scheduler.LAST_EXECUTED,
|
||||
comment: scheduler.EVENT_COMMENT,
|
||||
charset: scheduler.CHARACTER_SET_CLIENT,
|
||||
timezone: scheduler.TIME_ZONE
|
||||
};
|
||||
});
|
||||
|
||||
// TRIGGERS
|
||||
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.Database).map(trigger => {
|
||||
return {
|
||||
name: trigger.Trigger,
|
||||
statement: trigger.Statement,
|
||||
timing: trigger.Timing,
|
||||
definer: trigger.Definer,
|
||||
event: trigger.Event,
|
||||
table: trigger.Table,
|
||||
sqlMode: trigger.sql_mode,
|
||||
created: trigger.Created,
|
||||
charset: trigger.character_set_client
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: db.Database,
|
||||
tables: remappedTables,
|
||||
functions: remappedFunctions,
|
||||
procedures: remappedProcedures,
|
||||
triggers: remappedTriggers,
|
||||
schedulers: remappedSchedulers
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -279,13 +383,13 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* SELECT `user`, `host`, IF(LENGTH(password)>0, password, authentication_string) AS `password` FROM `mysql`.`user`
|
||||
* SELECT `user`, `host`, authentication_string) AS `password` FROM `mysql`.`user`
|
||||
*
|
||||
* @returns {Array.<Object>} users list
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async getUsers () {
|
||||
const { rows } = await this.raw('SELECT `user`, `host`, IF(LENGTH(password)>0, password, authentication_string) AS `password` FROM `mysql`.`user`');
|
||||
const { rows } = await this.raw('SELECT `user`, `host`, authentication_string AS `password` FROM `mysql`.`user`');
|
||||
|
||||
return rows.map(row => {
|
||||
return {
|
||||
@@ -674,7 +778,7 @@ export class MySQLClient extends AntaresCore {
|
||||
const results = await this.raw(sql);
|
||||
|
||||
return results.rows.map(row => {
|
||||
const schedule = row['Create Event'].match(/(?<=ON SCHEDULE\n*?\s*?).*?(?=\n)/gs)[0];
|
||||
const schedule = row['Create Event'];
|
||||
const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
|
||||
const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
|
||||
const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
|
||||
@@ -683,7 +787,7 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
return {
|
||||
definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||
sql: row['Create Event'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||
sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
|
||||
name: row.Event,
|
||||
comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||
state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
|
||||
@@ -863,7 +967,8 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
// ADD FIELDS
|
||||
additions.forEach(addition => {
|
||||
const length = addition.numLength || addition.charLength || addition.datePrecision;
|
||||
const typeInfo = this._getTypeInfo(addition.type);
|
||||
const length = typeInfo.length ? addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||
|
||||
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
@@ -900,7 +1005,8 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
// CHANGE FIELDS
|
||||
changes.forEach(change => {
|
||||
const length = change.numLength || change.charLength || change.datePrecision;
|
||||
const typeInfo = this._getTypeInfo(change.type);
|
||||
const length = typeInfo.length ? change.numLength || change.charLength || change.datePrecision : false;
|
||||
|
||||
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
||||
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
@@ -1069,14 +1175,12 @@ export class MySQLClient extends AntaresCore {
|
||||
const nestTables = args.nest ? '.' : false;
|
||||
const resultsArr = [];
|
||||
let paramsArr = [];
|
||||
let selectedFields = [];
|
||||
const queries = args.split ? sql.split(';') : [sql];
|
||||
|
||||
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) => {
|
||||
@@ -1086,32 +1190,34 @@ export class MySQLClient extends AntaresCore {
|
||||
if (err)
|
||||
reject(err);
|
||||
else {
|
||||
const remappedFields = fields
|
||||
let remappedFields = fields
|
||||
? fields.map(field => {
|
||||
if (!field || Array.isArray(field))
|
||||
return false;
|
||||
|
||||
const type = this._getType(field);
|
||||
|
||||
return {
|
||||
name: field.name,
|
||||
name: field.orgName,
|
||||
alias: field.name,
|
||||
orgName: field.orgName,
|
||||
schema: field.db,
|
||||
table: field.table,
|
||||
tableAlias: field.table,
|
||||
zerofill: field.zerofill,
|
||||
orgTable: field.orgTable,
|
||||
type: 'VARCHAR'
|
||||
type: type.name,
|
||||
length: type.length
|
||||
};
|
||||
})
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
|
||||
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
|
||||
if (field.orgTable) cachedTable = field.orgTable;// Needed for some queries on information_schema
|
||||
return {
|
||||
table: field.orgTable || cachedTable,
|
||||
schema: field.schema || 'INFORMATION_SCHEMA'
|
||||
@@ -1119,43 +1225,16 @@ export class MySQLClient extends AntaresCore {
|
||||
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
||||
|
||||
for (const paramObj of paramsArr) {
|
||||
try { // Table data
|
||||
if (!paramObj.table || !paramObj.schema) continue;
|
||||
|
||||
try { // Column details
|
||||
const response = await this.getTableColumns(paramObj);
|
||||
|
||||
let detailedFields = response.length
|
||||
? selectedFields.map(selField => {
|
||||
return response.find(field => field.name.toLowerCase() === selField.name.toLowerCase() && field.table === selField.table);
|
||||
}).filter(el => !!el)
|
||||
: [];
|
||||
|
||||
if (selectedFields.length) {
|
||||
detailedFields = detailedFields.map(field => {
|
||||
const aliasObj = remappedFields.find(resField => resField.orgName === field.name && resField.orgTable === field.table);
|
||||
return {
|
||||
...field,
|
||||
alias: aliasObj.name || field.name,
|
||||
tableAlias: aliasObj.table || field.table
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (!detailedFields.length) {
|
||||
detailedFields = remappedFields.map(field => {
|
||||
const isInFields = fieldsArr.some(f => field.name.toLowerCase() === f.name.toLowerCase() && field.table === f.table);
|
||||
|
||||
if (!isInFields) {
|
||||
return {
|
||||
...field,
|
||||
alias: field.name,
|
||||
tableAlias: field.table
|
||||
};
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
fieldsArr = fieldsArr ? [...fieldsArr, ...detailedFields] : detailedFields;
|
||||
remappedFields = remappedFields.map(field => {
|
||||
const detailedField = response.find(f => f.name === field.name);
|
||||
if (field.orgTable === paramObj.table && field.schema === paramObj.schema && detailedField.name === field.orgName)
|
||||
field = { ...detailedField, ...field };
|
||||
return field;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
@@ -1173,9 +1252,9 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
resolve({
|
||||
rows: Array.isArray(queryResult) ? queryResult : false,
|
||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||
report: !Array.isArray(queryResult) ? queryResult : false,
|
||||
fields: fieldsArr.length ? fieldsArr : remappedFields,
|
||||
fields: remappedFields,
|
||||
keys: keysArr
|
||||
});
|
||||
}
|
||||
|
22
src/renderer/components/BaseLoader.vue
Normal file
22
src/renderer/components/BaseLoader.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="empty">
|
||||
<div class="loading loading-lg" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseLoader'
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.empty {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
justify-content: center;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
@@ -253,7 +253,7 @@ export default {
|
||||
},
|
||||
fieldLength (field) {
|
||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||
return field.numLength || field.datePrecision || field.charLength || 0;
|
||||
return field.length;
|
||||
},
|
||||
inputProps (field) {
|
||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||
|
@@ -81,6 +81,34 @@ export default {
|
||||
})
|
||||
: [];
|
||||
},
|
||||
functions () {
|
||||
return this.workspace
|
||||
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||
.reduce((acc, curr) => {
|
||||
acc.push(...curr.functions);
|
||||
return acc;
|
||||
}, []).map(func => {
|
||||
return {
|
||||
name: `${func.name}()`,
|
||||
type: 'function'
|
||||
};
|
||||
})
|
||||
: [];
|
||||
},
|
||||
schedulers () {
|
||||
return this.workspace
|
||||
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||
.reduce((acc, curr) => {
|
||||
acc.push(...curr.schedulers);
|
||||
return acc;
|
||||
}, []).map(scheduler => {
|
||||
return {
|
||||
name: scheduler.name,
|
||||
type: 'scheduler'
|
||||
};
|
||||
})
|
||||
: [];
|
||||
},
|
||||
mode () {
|
||||
switch (this.workspace.client) {
|
||||
case 'mysql':
|
||||
@@ -160,7 +188,9 @@ export default {
|
||||
[
|
||||
...this.tables,
|
||||
...this.triggers,
|
||||
...this.procedures
|
||||
...this.procedures,
|
||||
...this.functions,
|
||||
...this.schedulers
|
||||
].forEach(el => {
|
||||
completions.push({
|
||||
value: el.name,
|
||||
|
@@ -3,10 +3,11 @@
|
||||
<summary
|
||||
class="accordion-header database-name"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
||||
@click="changeBreadcrumbs({schema: database.name, table: null})"
|
||||
@click="selectSchema(database.name)"
|
||||
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
||||
>
|
||||
<i class="icon mdi mdi-18px mdi-chevron-right" />
|
||||
<div v-if="isLoading" class="icon loading" />
|
||||
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
||||
<span>{{ database.name }}</span>
|
||||
</summary>
|
||||
@@ -161,13 +162,22 @@ export default {
|
||||
database: Object,
|
||||
connection: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getLoadedSchemas: 'workspaces/getLoadedSchemas',
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
breadcrumbs () {
|
||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
||||
},
|
||||
loadedSchemas () {
|
||||
return this.getLoadedSchemas(this.connection.uid);
|
||||
},
|
||||
maxSize () {
|
||||
return this.database.tables.reduce((acc, curr) => {
|
||||
if (curr.size > acc) acc = curr.size;
|
||||
@@ -180,9 +190,19 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||
refreshSchema: 'workspaces/refreshSchema'
|
||||
}),
|
||||
formatBytes,
|
||||
async selectSchema (schema) {
|
||||
if (!this.loadedSchemas.has(schema)) {
|
||||
this.isLoading = true;
|
||||
await this.refreshSchema({ uid: this.connection.uid, schema });
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
this.changeBreadcrumbs({ schema, table: null });
|
||||
},
|
||||
showDatabaseContext (event, database) {
|
||||
this.changeBreadcrumbs({ schema: database, table: null });
|
||||
this.$emit('show-database-context', { event, database });
|
||||
@@ -230,6 +250,16 @@ export default {
|
||||
.misc-icon {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.loading {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
|
||||
&::after {
|
||||
height: 0.6rem;
|
||||
width: 0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.misc-name {
|
||||
|
@@ -51,7 +51,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12">
|
||||
<div class="workspace-query-results column col-12 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<WorkspacePropsTable
|
||||
v-if="localFields"
|
||||
ref="indexTable"
|
||||
@@ -106,6 +107,7 @@
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import Tables from '@/ipc-api/Tables';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import WorkspacePropsTable from '@/components/WorkspacePropsTable';
|
||||
import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal';
|
||||
import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal';
|
||||
@@ -114,6 +116,7 @@ import WorkspacePropsForeignModal from '@/components/WorkspacePropsForeignModal'
|
||||
export default {
|
||||
name: 'WorkspacePropsTab',
|
||||
components: {
|
||||
BaseLoader,
|
||||
WorkspacePropsTable,
|
||||
WorkspacePropsOptionsModal,
|
||||
WorkspacePropsIndexesModal,
|
||||
@@ -126,7 +129,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
isOptionsModal: false,
|
||||
isIndexesModal: false,
|
||||
@@ -205,8 +208,10 @@ export default {
|
||||
}),
|
||||
async getFieldsData () {
|
||||
if (!this.table) return;
|
||||
|
||||
this.localFields = [];
|
||||
this.newFieldsCounter = 0;
|
||||
this.isQuering = true;
|
||||
this.isLoading = true;
|
||||
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
||||
|
||||
const params = {
|
||||
@@ -281,7 +286,7 @@ export default {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -43,7 +43,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
@@ -74,6 +75,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import WorkspacePropsFunctionOptionsModal from '@/components/WorkspacePropsFunctionOptionsModal';
|
||||
import WorkspacePropsFunctionParamsModal from '@/components/WorkspacePropsFunctionParamsModal';
|
||||
@@ -82,6 +84,7 @@ import Functions from '@/ipc-api/Functions';
|
||||
export default {
|
||||
name: 'WorkspacePropsTabFunction',
|
||||
components: {
|
||||
BaseLoader,
|
||||
QueryEditor,
|
||||
WorkspacePropsFunctionOptionsModal,
|
||||
WorkspacePropsFunctionParamsModal
|
||||
@@ -93,7 +96,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
isOptionsModal: false,
|
||||
isParamsModal: false,
|
||||
@@ -166,7 +169,9 @@ export default {
|
||||
}),
|
||||
async getFunctionData () {
|
||||
if (!this.function) return;
|
||||
this.isQuering = true;
|
||||
|
||||
this.isLoading = true;
|
||||
this.localFunction = { sql: '' };
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -189,7 +194,7 @@ export default {
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -43,7 +43,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
@@ -75,6 +76,7 @@
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal';
|
||||
import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal';
|
||||
import Routines from '@/ipc-api/Routines';
|
||||
@@ -83,6 +85,7 @@ export default {
|
||||
name: 'WorkspacePropsTabRoutine',
|
||||
components: {
|
||||
QueryEditor,
|
||||
BaseLoader,
|
||||
WorkspacePropsRoutineOptionsModal,
|
||||
WorkspacePropsRoutineParamsModal
|
||||
},
|
||||
@@ -93,7 +96,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
isOptionsModal: false,
|
||||
isParamsModal: false,
|
||||
@@ -166,7 +169,8 @@ export default {
|
||||
}),
|
||||
async getRoutineData () {
|
||||
if (!this.routine) return;
|
||||
this.isQuering = true;
|
||||
this.localRoutine = { sql: '' };
|
||||
this.isLoading = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -189,7 +193,7 @@ export default {
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -114,7 +114,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.schedulerBody') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
@@ -137,6 +138,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import WorkspacePropsSchedulerTimingModal from '@/components/WorkspacePropsSchedulerTimingModal';
|
||||
import Schedulers from '@/ipc-api/Schedulers';
|
||||
@@ -144,6 +146,7 @@ import Schedulers from '@/ipc-api/Schedulers';
|
||||
export default {
|
||||
name: 'WorkspacePropsTabScheduler',
|
||||
components: {
|
||||
BaseLoader,
|
||||
QueryEditor,
|
||||
WorkspacePropsSchedulerTimingModal
|
||||
},
|
||||
@@ -154,7 +157,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
isTimingModal: false,
|
||||
originalScheduler: null,
|
||||
@@ -226,7 +229,7 @@ export default {
|
||||
}),
|
||||
async getSchedulerData () {
|
||||
if (!this.scheduler) return;
|
||||
this.isQuering = true;
|
||||
this.isLoading = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -249,7 +252,7 @@ export default {
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -95,7 +95,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
@@ -110,13 +111,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import Triggers from '@/ipc-api/Triggers';
|
||||
|
||||
export default {
|
||||
name: 'WorkspacePropsTabTrigger',
|
||||
components: {
|
||||
BaseLoader,
|
||||
QueryEditor
|
||||
},
|
||||
props: {
|
||||
@@ -126,7 +129,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
originalTrigger: null,
|
||||
localTrigger: { sql: '' },
|
||||
@@ -197,7 +200,9 @@ export default {
|
||||
}),
|
||||
async getTriggerData () {
|
||||
if (!this.trigger) return;
|
||||
this.isQuering = true;
|
||||
|
||||
this.localTrigger = { sql: '' };
|
||||
this.isLoading = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -220,7 +225,7 @@ export default {
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -156,7 +156,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
@@ -172,12 +173,14 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import Views from '@/ipc-api/Views';
|
||||
|
||||
export default {
|
||||
name: 'WorkspacePropsTabView',
|
||||
components: {
|
||||
BaseLoader,
|
||||
QueryEditor
|
||||
},
|
||||
props: {
|
||||
@@ -187,7 +190,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
originalView: null,
|
||||
localView: { sql: '' },
|
||||
@@ -251,7 +254,8 @@ export default {
|
||||
}),
|
||||
async getViewData () {
|
||||
if (!this.view) return;
|
||||
this.isQuering = true;
|
||||
this.isLoading = true;
|
||||
this.localView = { sql: '' };
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -274,7 +278,7 @@ export default {
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
|
@@ -196,7 +196,7 @@ export default {
|
||||
},
|
||||
fieldLength (field) {
|
||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||
return field.numLength || field.datePrecision || field.charLength || 0;
|
||||
return field.length;
|
||||
},
|
||||
keyName (key) {
|
||||
switch (key) {
|
||||
|
@@ -18,8 +18,8 @@ export default class {
|
||||
return ipcRenderer.invoke('delete-database', params);
|
||||
}
|
||||
|
||||
static getStructure (uid) {
|
||||
return ipcRenderer.invoke('get-structure', uid);
|
||||
static getStructure (params) {
|
||||
return ipcRenderer.invoke('get-structure', params);
|
||||
}
|
||||
|
||||
static getCollations (uid) {
|
||||
|
@@ -1245,6 +1245,12 @@ ace.define('ace/autocomplete/popup', ['require', 'exports', 'module', 'ace/virtu
|
||||
case 'routine':
|
||||
iconClass = 'mdi-sync-circle';
|
||||
break;
|
||||
case 'function':
|
||||
iconClass = 'mdi-arrow-right-bold-box';
|
||||
break;
|
||||
case 'scheduler':
|
||||
iconClass = 'mdi-calendar-clock';
|
||||
break;
|
||||
case 'keyword':
|
||||
iconClass = 'mdi-cube';
|
||||
break;
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import VuexPersist from 'vuex-persist';
|
||||
|
||||
import application from './modules/application.store';
|
||||
import settings from './modules/settings.store';
|
||||
@@ -12,15 +11,6 @@ import notifications from './modules/notifications.store';
|
||||
|
||||
import ipcUpdates from './plugins/ipcUpdates';
|
||||
|
||||
const vuexLocalStorage = new VuexPersist({
|
||||
key: 'application', // The key to store the state on in the storage provider.
|
||||
storage: window.localStorage,
|
||||
reducer: state => ({
|
||||
connections: state.connections,
|
||||
settings: state.settings
|
||||
})
|
||||
});
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
@@ -33,7 +23,6 @@ export default new Vuex.Store({
|
||||
notifications
|
||||
},
|
||||
plugins: [
|
||||
vuexLocalStorage.plugin,
|
||||
ipcUpdates
|
||||
]
|
||||
});
|
||||
|
@@ -40,6 +40,9 @@ export default {
|
||||
.filter(workspace => workspace.connected)
|
||||
.map(workspace => workspace.uid);
|
||||
},
|
||||
getLoadedSchemas: state => uid => {
|
||||
return state.workspaces.find(workspace => workspace.uid === uid).loaded_schemas;
|
||||
},
|
||||
isUnsavedDiscardModal: state => {
|
||||
return state.is_unsaved_discard_modal;
|
||||
}
|
||||
@@ -65,6 +68,8 @@ export default {
|
||||
? {
|
||||
...workspace,
|
||||
structure: {},
|
||||
breadcrumbs: {},
|
||||
loaded_schemas: new Set(),
|
||||
connected: false
|
||||
}
|
||||
: workspace);
|
||||
@@ -77,6 +82,19 @@ export default {
|
||||
}
|
||||
: workspace);
|
||||
},
|
||||
REFRESH_SCHEMA (state, { uid, schema, schemaElements }) {
|
||||
state.workspaces = state.workspaces.map(workspace => {
|
||||
if (workspace.uid === uid) {
|
||||
const schemaIndex = workspace.structure.findIndex(s => s.name === schema);
|
||||
|
||||
if (schemaIndex !== -1)
|
||||
workspace.structure[schemaIndex] = schemaElements;
|
||||
else
|
||||
workspace.structure.push(schemaElements);
|
||||
}
|
||||
return workspace;
|
||||
});
|
||||
},
|
||||
REFRESH_COLLATIONS (state, { uid, collations }) {
|
||||
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
|
||||
? {
|
||||
@@ -198,6 +216,13 @@ export default {
|
||||
},
|
||||
SET_PENDING_BREADCRUMBS (state, payload) {
|
||||
state.pending_breadcrumbs = payload;
|
||||
},
|
||||
ADD_LOADED_SCHEMA (state, payload) {
|
||||
state.workspaces = state.workspaces.map(workspace => {
|
||||
if (workspace.uid === payload.uid)
|
||||
workspace.loaded_schemas.add(payload.schema);
|
||||
return workspace;
|
||||
});
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -237,9 +262,10 @@ export default {
|
||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||
}
|
||||
},
|
||||
async refreshStructure ({ dispatch, commit }, uid) {
|
||||
async refreshStructure ({ dispatch, commit, getters }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getStructure(uid);
|
||||
const { status, response } = await Database.getStructure({ uid, schemas: getters.getLoadedSchemas(uid) });
|
||||
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
@@ -249,6 +275,18 @@ export default {
|
||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||
}
|
||||
},
|
||||
async refreshSchema ({ dispatch, commit }, { uid, schema }) {
|
||||
try {
|
||||
const { status, response } = await Database.getStructure({ uid, schemas: new Set([schema]) });
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
commit('REFRESH_SCHEMA', { uid, schema, schemaElements: response.find(_schema => _schema.name === schema) });
|
||||
}
|
||||
catch (err) {
|
||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||
}
|
||||
},
|
||||
async refreshCollations ({ dispatch, commit }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getCollations(uid);
|
||||
@@ -312,7 +350,8 @@ export default {
|
||||
variables: [],
|
||||
collations: [],
|
||||
users: [],
|
||||
breadcrumbs: {}
|
||||
breadcrumbs: {},
|
||||
loaded_schemas: new Set()
|
||||
};
|
||||
|
||||
commit('ADD_WORKSPACE', workspace);
|
||||
@@ -349,6 +388,9 @@ export default {
|
||||
|
||||
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
|
||||
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
||||
|
||||
if (payload.schema)
|
||||
commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema: payload.schema });
|
||||
},
|
||||
newTab ({ commit }, uid) {
|
||||
const tab = uidGen('T');
|
||||
|
Reference in New Issue
Block a user