diff --git a/src/main/ipc-handlers/database.js b/src/main/ipc-handlers/database.js index 9f5021a4..95f9e8f7 100644 --- a/src/main/ipc-handlers/database.js +++ b/src/main/ipc-handlers/database.js @@ -83,6 +83,17 @@ export default connections => { } }); + ipcMain.handle('get-engines', async (event, uid) => { + try { + const result = await connections[uid].getEngines(); + + return { status: 'success', response: result }; + } + catch (err) { + return { status: 'error', response: err.toString() }; + } + }); + ipcMain.handle('use-schema', async (event, { uid, schema }) => { if (!schema) return; diff --git a/src/main/libs/clients/MySQLClient.js b/src/main/libs/clients/MySQLClient.js index 1a1a4e87..59a387fc 100644 --- a/src/main/libs/clients/MySQLClient.js +++ b/src/main/libs/clients/MySQLClient.js @@ -267,6 +267,28 @@ export class MySQLClient extends AntaresCore { }); } + /** + * SHOW ENGINES + * + * @returns {Array.} engines list + * @memberof MySQLClient + */ + async getEngines () { + const sql = 'SHOW ENGINES'; + const results = await this.raw(sql); + + return results.rows.map(row => { + return { + name: row.Engine, + support: row.Support, + comment: row.Comment, + transactions: row.Trasactions, + xa: row.XA, + savepoints: row.Savepoints + }; + }); + } + /** * ALTER TABLE * @@ -278,12 +300,19 @@ export class MySQLClient extends AntaresCore { table, additions, deletions, - changes + changes, + options } = params; let sql = `ALTER TABLE \`${table}\` `; const alterColumns = []; + // OPTIONS + if ('comment' in options) alterColumns.push(`COMMENT='${options.comment}'`); + if ('engine' in options) alterColumns.push(`ENGINE=${options.engine}`); + if ('autoIncrement' in options) alterColumns.push(`AUTO_INCREMENT=${+options.autoIncrement}`); + if ('collation' in options) alterColumns.push(`COLLATE='${options.collation}'`); + // ADD additions.forEach(addition => { const length = addition.numLength || addition.charLength || addition.datePrecision; @@ -325,6 +354,9 @@ export class MySQLClient extends AntaresCore { sql += alterColumns.join(', '); + // RENAME + if (options.name) sql += `; RENAME TABLE \`${table}\` TO \`${options.name}\``; + return await this.raw(sql); } diff --git a/src/renderer/components/WorkspaceExploreBarDatabaseContext.vue b/src/renderer/components/WorkspaceExploreBarDatabaseContext.vue index 7d141bcb..7a25afe6 100644 --- a/src/renderer/components/WorkspaceExploreBarDatabaseContext.vue +++ b/src/renderer/components/WorkspaceExploreBarDatabaseContext.vue @@ -3,10 +3,10 @@ :context-event="contextEvent" @close-context="closeContext" > - +
{{ $t('word.edit') }}
diff --git a/src/renderer/components/WorkspacePropsTab.vue b/src/renderer/components/WorkspacePropsTab.vue index 48770610..39af2933 100644 --- a/src/renderer/components/WorkspacePropsTab.vue +++ b/src/renderer/components/WorkspacePropsTab.vue @@ -40,7 +40,7 @@ {{ $t('word.foreignKeys') }} - @@ -60,6 +60,92 @@ @remove-field="removeField" /> + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
@@ -68,11 +154,13 @@ import { mapGetters, mapActions } from 'vuex'; import { uidGen } from 'common/libs/uidGen'; import Tables from '@/ipc-api/Tables'; import WorkspacePropsTable from '@/components/WorkspacePropsTable'; +import ConfirmModal from '@/components/BaseConfirmModal'; export default { name: 'WorkspacePropsTab', components: { - WorkspacePropsTable + WorkspacePropsTable, + ConfirmModal }, props: { connection: Object, @@ -83,21 +171,32 @@ export default { tabUid: 'prop', isQuering: false, isSaving: false, + isAddModal: false, + isOptionsModal: false, + isOptionsChanging: false, originalFields: [], localFields: [], originalKeyUsage: [], localKeyUsage: [], - lastTable: null, - isAddModal: false + localOptions: [], + lastTable: null }; }, computed: { ...mapGetters({ - getWorkspace: 'workspaces/getWorkspace' + getWorkspace: 'workspaces/getWorkspace', + getDatabaseVariable: 'workspaces/getDatabaseVariable' }), workspace () { return this.getWorkspace(this.connection.uid); }, + tableOptions () { + const db = this.workspace.structure.find(db => db.name === this.schema); + return db ? db.tables.find(table => table.name === this.table) : {}; + }, + defaultEngine () { + return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || ''; + }, isSelected () { return this.workspace.selected_tab === 'prop'; }, @@ -105,7 +204,12 @@ export default { return this.workspace.breadcrumbs.schema; }, isChanged () { - return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) || JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage); + return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) || + JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) || + JSON.stringify(this.tableOptions) !== JSON.stringify(this.localOptions); + }, + isTableNameValid () { + return this.localOptions.name !== ''; } }, watch: { @@ -124,11 +228,13 @@ export default { }, methods: { ...mapActions({ - addNotification: 'notifications/addNotification' + addNotification: 'notifications/addNotification', + refreshStructure: 'workspaces/refreshStructure' }), async getFieldsData () { if (!this.table) return; this.isQuering = true; + this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); const params = { uid: this.connection.uid, @@ -197,20 +303,30 @@ export default { if (this.localFields[lI]) changes.push({ ...this.localFields[lI], after, orgName }); }); + // OPTIONS + const options = Object.keys(this.localOptions).reduce((acc, option) => { + if (this.localOptions[option] !== this.tableOptions[option]) + acc[option] = this.localOptions[option]; + return acc; + }, {}); + const params = { uid: this.connection.uid, schema: this.schema, table: this.workspace.breadcrumbs.table, additions, changes, - deletions + deletions, + options }; try { // Key usage (foreign keys) const { status, response } = await Tables.alterTable(params); - if (status === 'success') + if (status === 'success') { + await this.refreshStructure(this.connection.uid); this.getFieldsData(); + } else this.addNotification({ status: 'error', message: response }); } @@ -223,6 +339,7 @@ export default { clearChanges () { this.localFields = JSON.parse(JSON.stringify(this.originalFields)); this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage)); + this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); }, addField () { this.localFields.push({ @@ -256,6 +373,22 @@ export default { }, hideAddModal () { this.isAddModal = false; + }, + showOptionsModal () { + this.isOptionsModal = true; + }, + hideOptionsModal () { + if (this.isOptionsChanging && !this.isTableNameValid) { + this.isOptionsChanging = false; + return; + } + + this.isOptionsModal = false; + if (!this.isOptionsChanging) this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); + this.isOptionsChanging = false; + }, + confirmOptionsChange () { + this.isOptionsChanging = true; } } }; diff --git a/src/renderer/i18n/en-US.js b/src/renderer/i18n/en-US.js index 174d85de..f9d44526 100644 --- a/src/renderer/i18n/en-US.js +++ b/src/renderer/i18n/en-US.js @@ -53,7 +53,9 @@ module.exports = { comment: 'Comment', key: 'Key | Keys', order: 'Order', - expression: 'Expression' + expression: 'Expression', + autoIncrement: 'Auto Increment', + engine: 'Engine' }, message: { appWelcome: 'Welcome to Antares SQL Client!', diff --git a/src/renderer/ipc-api/Database.js b/src/renderer/ipc-api/Database.js index 6e1ac288..db40e370 100644 --- a/src/renderer/ipc-api/Database.js +++ b/src/renderer/ipc-api/Database.js @@ -30,6 +30,10 @@ export default class { return ipcRenderer.invoke('get-variables', uid); } + static getEngines (uid) { + return ipcRenderer.invoke('get-engines', uid); + } + static useSchema (params) { return ipcRenderer.invoke('use-schema', params); } diff --git a/src/renderer/scss/main.scss b/src/renderer/scss/main.scss index 61cb6cfb..24d3f62c 100644 --- a/src/renderer/scss/main.scss +++ b/src/renderer/scss/main.scss @@ -191,6 +191,11 @@ body { background-color: $bg-color-gray; } +.form-input.is-error, +.form-select.is-error { + background-color: $bg-color-gray; +} + .form-input:not(:placeholder-shown):invalid:focus { background: $bg-color-gray; } diff --git a/src/renderer/store/modules/workspaces.store.js b/src/renderer/store/modules/workspaces.store.js index dc8ec5a6..ba967ab7 100644 --- a/src/renderer/store/modules/workspaces.store.js +++ b/src/renderer/store/modules/workspaces.store.js @@ -85,6 +85,14 @@ export default { } : workspace); }, + REFRESH_ENGINES (state, { uid, engines }) { + state.workspaces = state.workspaces.map(workspace => workspace.uid === uid + ? { + ...workspace, + engines + } + : workspace); + }, ADD_WORKSPACE (state, workspace) { state.workspaces.push(workspace); }, @@ -194,6 +202,7 @@ export default { }); dispatch('refreshCollations', connection.uid); dispatch('refreshVariables', connection.uid); + dispatch('refreshEngines', connection.uid); } } catch (err) { @@ -236,6 +245,18 @@ export default { dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true }); } }, + async refreshEngines ({ dispatch, commit }, uid) { + try { + const { status, response } = await Database.getEngines(uid); + if (status === 'error') + dispatch('notifications/addNotification', { status, message: response }, { root: true }); + else + commit('REFRESH_ENGINES', { uid, engines: response }); + } + catch (err) { + dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true }); + } + }, removeConnected ({ commit }, uid) { Connection.disconnect(uid); commit('REMOVE_CONNECTED', uid);