feat: edit database collation

This commit is contained in:
Fabio 2020-10-03 12:11:42 +02:00
parent 4288a1fd33
commit 54717e1f6a
7 changed files with 235 additions and 17 deletions

View File

@ -14,6 +14,18 @@ export default connections => {
}
});
ipcMain.handle('update-database', async (event, params) => {
try {
const query = `ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`;
await connections[params.uid].raw(query);
return { status: 'success' };
}
catch (err) {
return { status: 'error', response: err.toString() };
}
});
ipcMain.handle('delete-database', async (event, params) => {
try {
const query = `DROP DATABASE \`${params.database}\``;
@ -26,6 +38,18 @@ export default connections => {
}
});
ipcMain.handle('get-database-collation', async (event, params) => {
try {
const query = `SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`;
const collation = await connections[params.uid].raw(query);
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
}
catch (err) {
return { status: 'error', response: err.toString() };
}
});
ipcMain.handle('get-structure', async (event, uid) => {
try {
const structure = await connections[uid].getStructure();

View File

@ -36,6 +36,7 @@ export class MySQLClient extends AntaresCore {
*/
async getStructure () {
const { rows: databases } = await this.raw('SHOW DATABASES');
// TODO: SHOW TABLE STATUS FROM `{DATABASE_NAME}`;
const { rows: tables } = await this
.select('*')
@ -44,10 +45,30 @@ export class MySQLClient extends AntaresCore {
.orderBy({ TABLE_SCHEMA: 'ASC', TABLE_NAME: 'ASC' })
.run();
return databases.map(db => {
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
const { rows: schedulers } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
const triggersArr = [];
for (const db of databases) {
let { rows: triggers } = await this.raw(`SHOW TRIGGERS FROM \`${db.Database}\``);
if (triggers.length) {
triggers = triggers.map(trigger => {
trigger.Db = db.Database;
return trigger;
});
triggersArr.push(...triggers);
}
}
return databases.map(db => { // TODO: remap all objects,
return {
name: db.Database,
tables: tables.filter(table => table.TABLE_SCHEMA === db.Database)// TODO: remap tables objects
tables: tables.filter(table => table.TABLE_SCHEMA === db.Database),
functions: functions.filter(func => func.Db === db.Database),
procedures: procedures.filter(procedure => procedure.Db === db.Database),
triggers: triggersArr.filter(trigger => trigger.Db === db.Database),
schedulers: schedulers.filter(scheduler => scheduler.Db === db.Database)
};
});
}

View File

@ -0,0 +1,151 @@
<template>
<div class="modal active">
<a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-container p-0">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editDatabase') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div>
<div class="modal-body pb-0">
<div class="content">
<form class="form-horizontal">
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.name') }}:</label>
</div>
<div class="col-9">
<input
v-model="database.name"
class="form-input"
type="text"
required
:placeholder="$t('message.databaseName')"
readonly
>
</div>
</div>
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.collation') }}:</label>
</div>
<div class="col-9">
<select v-model="database.collation" class="form-select">
<option
v-for="collation in collations"
:key="collation.id"
:value="collation.collation"
>
{{ collation.collation }}
</option>
</select>
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
</div>
</div>
</form>
</div>
</div>
<div class="modal-footer text-light">
<button class="btn btn-primary mr-2" @click.stop="updateDatabase">
{{ $t('word.update') }}
</button>
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import Database from '@/ipc-api/Database';
export default {
name: 'ModalEditDatabase',
props: {
selectedDatabase: String
},
data () {
return {
database: {
name: '',
prevName: '',
collation: ''
}
};
},
computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
collations () {
return this.getWorkspace(this.selectedWorkspace).collations;
},
defaultCollation () {
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
}
},
async created () {
let actualCollation;
try {
const { status, response } = await Database.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
if (status === 'success')
actualCollation = response;
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.database = {
name: this.selectedDatabase,
prevName: this.selectedDatabase,
collation: actualCollation || this.defaultCollation,
prevCollation: actualCollation || this.defaultCollation
};
},
methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
async updateDatabase () {
if (this.database.collation !== this.database.prevCollation) {
try {
const { status, response } = await Database.updateDatabase({
uid: this.selectedWorkspace,
...this.database
});
if (status === 'success')
this.closeModal();
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
}
else
this.closeModal();
},
closeModal () {
this.$emit('close');
}
}
};
</script>
<style scoped>
.modal-container {
max-width: 360px;
}
</style>

View File

@ -64,7 +64,7 @@ import { mapGetters, mapActions } from 'vuex';
import Database from '@/ipc-api/Database';
export default {
name: 'ModalNewDB',
name: 'ModalNewDatabase',
data () {
return {
database: {

View File

@ -42,7 +42,7 @@
/>
</div>
</div>
<ModalNewDB
<ModalNewDatabase
v-if="isNewDBModal"
@close="hideNewDBModal"
@reload="refresh"
@ -63,7 +63,7 @@ import _ from 'lodash';
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
import ModalNewDB from '@/components/ModalNewDB';
import ModalNewDatabase from '@/components/ModalNewDatabase';
export default {
name: 'WorkspaceExploreBar',
@ -71,7 +71,7 @@ export default {
WorkspaceConnectPanel,
WorkspaceExploreBarDatabase,
DatabaseContext,
ModalNewDB
ModalNewDatabase
},
props: {
connection: Object,
@ -131,7 +131,6 @@ export default {
changeExplorebarSize: 'settings/changeExplorebarSize'
}),
async refresh () {
console.log('refresh');
if (!this.isRefreshing) {
this.isRefreshing = true;
await this.refreshStructure(this.connection.uid);

View File

@ -7,17 +7,17 @@
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span>
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
</div> -->
<div class="context-element">
<div class="context-element" @click="showEditModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span>
</div>
<div class="context-element" @click="showConfirmModal">
<div class="context-element" @click="showDeleteModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
</div>
<ConfirmModal
v-if="isConfirmModal"
v-if="isDeleteModal"
@confirm="deleteDatabase"
@hide="hideConfirmModal"
@hide="hideDeleteModal"
>
<template slot="header">
<div class="d-flex">
@ -30,6 +30,11 @@
</div>
</div>
</ConfirmModal>
<ModalEditDatabase
v-if="isEditModal"
:selected-database="selectedDatabase"
@close="hideEditModal"
/>
</BaseContextMenu>
</template>
@ -37,13 +42,15 @@
import { mapGetters, mapActions } from 'vuex';
import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal';
import ModalEditDatabase from '@/components/ModalEditDatabase';
import Database from '@/ipc-api/Database';
export default {
name: 'WorkspaceExploreBarDatabaseContext',
components: {
BaseContextMenu,
ConfirmModal
ConfirmModal,
ModalEditDatabase
},
props: {
contextEvent: MouseEvent,
@ -51,7 +58,8 @@ export default {
},
data () {
return {
isConfirmModal: false
isDeleteModal: false,
isEditModal: false
};
},
computed: {
@ -65,11 +73,18 @@ export default {
showEditModal: 'application/showEditConnModal',
addNotification: 'notifications/addNotification'
}),
showConfirmModal () {
this.isConfirmModal = true;
showDeleteModal () {
this.isDeleteModal = true;
},
hideConfirmModal () {
this.isConfirmModal = false;
hideDeleteModal () {
this.isDeleteModal = false;
},
showEditModal () {
this.isEditModal = true;
},
hideEditModal () {
this.isEditModal = false;
this.closeContext();
},
closeContext () {
this.$emit('close-context');

View File

@ -6,6 +6,14 @@ export default class {
return ipcRenderer.invoke('create-database', params);
}
static updateDatabase (params) {
return ipcRenderer.invoke('update-database', params);
}
static getDatabaseCollation (params) {
return ipcRenderer.invoke('get-database-collation', params);
}
static deleteDatabase (params) {
return ipcRenderer.invoke('delete-database', params);
}