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

Compare commits

...

19 Commits

Author SHA1 Message Date
71910ca5c8 chore(release): 0.1.12 2021-06-09 09:03:18 +02:00
cce5adbac7 feat(PostgreSQL): trigger rename and delete 2021-06-08 09:12:43 +02:00
e6d67fcb0c Update README.md 2021-06-07 23:03:08 +02:00
e83b80afc9 chore: update README.md 2021-06-05 12:27:20 +02:00
8742fa10f0 fix: internal exceptions 2021-06-05 10:15:44 +02:00
ab54eb086f chore: update dependencies 2021-06-05 10:15:08 +02:00
308f69f12e chore: update README.md 2021-06-04 14:01:46 +02:00
2f30bafa33 Merge pull request #74 from digitalgopnik/master
feat: added translation for de-DE
2021-06-04 10:39:22 +02:00
Christian
1716077182 added translation for de-DE 2021-06-04 09:37:02 +02:00
5562e73e75 fix: page offset not reset when selected table changes 2021-06-03 11:02:24 +02:00
8a7cc2a14f fix(UI): unable to browse view's result pages 2021-06-03 10:57:57 +02:00
9ca059d979 fix(MySQL): view's data tab doesn't work with some views, closes #71 2021-06-03 10:54:59 +02:00
c9ccf786cd chore(release): 0.1.11 2021-06-02 15:47:12 +02:00
5e9c88a7fd fix(UI): prevent canc key to trigger delete modal when editing a row 2021-06-02 12:32:12 +02:00
e0e6483d1f build: update dependencies 2021-06-02 12:13:43 +02:00
66227569f4 fix: table row loses internal id after cell update 2021-06-02 11:58:34 +02:00
faa799c8ea fix(MySQL): missing schema altering tables in some conditions 2021-05-31 17:07:48 +02:00
acc1eeb094 fix: empty offset in cell update queries 2021-05-31 14:27:02 +02:00
865dad80df chore: update README.md 2021-05-30 10:54:58 +02:00
18 changed files with 561 additions and 67 deletions

View File

@@ -2,6 +2,31 @@
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. 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.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
### Features
* **PostgreSQL:** trigger rename and delete ([cce5adb](https://github.com/Fabio286/antares/commit/cce5adbac75170186956211b54f4db5be25aba07))
### Bug Fixes
* internal exceptions ([8742fa1](https://github.com/Fabio286/antares/commit/8742fa10f08c92a98325df50be9a7df8e36b0f2c))
* page offset not reset when selected table changes ([5562e73](https://github.com/Fabio286/antares/commit/5562e73e75c659051a49d61041d1eee9d66ff6e2))
* **MySQL:** view's data tab doesn't work with some views, closes [#71](https://github.com/Fabio286/antares/issues/71) ([9ca059d](https://github.com/Fabio286/antares/commit/9ca059d979161c0132f64db372b4ed7ecc844b2d))
* **UI:** unable to browse view's result pages ([8a7cc2a](https://github.com/Fabio286/antares/commit/8a7cc2a14fa289c2f3d9e9eb1a5435ffbbaab006))
### [0.1.11](https://github.com/Fabio286/antares/compare/v0.1.10...v0.1.11) (2021-06-02)
### Bug Fixes
* **UI:** prevent canc key to trigger delete modal when editing a row ([5e9c88a](https://github.com/Fabio286/antares/commit/5e9c88a7fd656dd69b92ba4384ca81f4e8cdd422))
* table row loses internal id after cell update ([6622756](https://github.com/Fabio286/antares/commit/66227569f4c032298e20d44c253d30109ffde0b2))
* **MySQL:** missing schema altering tables in some conditions ([faa799c](https://github.com/Fabio286/antares/commit/faa799c8ea2329e7ca1b54c9414a060bc8a2a840))
* empty offset in cell update queries ([acc1eeb](https://github.com/Fabio286/antares/commit/acc1eeb0948db35f2c9a4d85d70fb654b41b15e5))
### [0.1.10](https://github.com/Fabio286/antares/compare/v0.1.9...v0.1.10) (2021-05-29) ### [0.1.10](https://github.com/Fabio286/antares/compare/v0.1.9...v0.1.10) (2021-05-29)

View File

@@ -14,7 +14,7 @@ Many of its current features are enough to have a pleasant user experience with
I'm actively working on it, hoping to provide cool features and fixes as soon as possible. I'm actively working on it, hoping to provide cool features and fixes as soon as possible.
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest). 🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
👁 To stay tuned for new releases watch this repo on **Release only** channel. 👁 To stay tuned for new releases [follow Antares SQL](https://twitter.com/AntaresSQL) on Twitter.
🌟 Don't forget to **leave a star** if you appreciate this project. 🌟 Don't forget to **leave a star** if you appreciate this project.
## Philosophy ## Philosophy
@@ -30,7 +30,9 @@ A modern application created with minimalism and semplicity in mind, with featur
## How to contribute ## How to contribute
- [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares) - 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide)
- 🚧 [Project Board](https://github.com/users/Fabio286/projects/1)
## Current main features ## Current main features
@@ -93,6 +95,7 @@ This is a roadmap with major features will come in near future.
**Spanish Translation** / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)] **Spanish Translation** / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
**French Translation** / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)] **French Translation** / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)]
**Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)] **Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)]
**Deutsch (Deutschland)** / [Christian Ratz](https://github.com/digitalgopnik) [[#74](https://github.com/Fabio286/antares/pull/74)]
## Reviews ## Reviews

View File

@@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.1.10", "version": "0.1.12",
"description": "A cross-platform easy to use SQL client.", "description": "A cross-platform easy to use SQL client.",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/Fabio286/antares.git", "repository": "https://github.com/Fabio286/antares.git",
@@ -94,7 +94,7 @@
"electron-store": "^8.0.0", "electron-store": "^8.0.0",
"electron-updater": "^4.3.9", "electron-updater": "^4.3.9",
"faker": "^5.5.3", "faker": "^5.5.3",
"marked": "^2.0.5", "marked": "^2.0.7",
"moment": "^2.29.1", "moment": "^2.29.1",
"mysql2": "^2.2.5", "mysql2": "^2.2.5",
"pg": "^8.5.1", "pg": "^8.5.1",
@@ -108,27 +108,27 @@
"vuex": "^3.6.2" "vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.14.3", "@babel/eslint-parser": "^7.14.4",
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"electron": "^13.0.0", "electron": "^13.1.1",
"electron-builder": "22.10.5", "electron-builder": "22.10.5",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-webpack": "^2.8.2", "electron-webpack": "^2.8.2",
"electron-webpack-vue": "^2.4.0", "electron-webpack-vue": "^2.4.0",
"eslint": "^7.27.0", "eslint": "^7.28.0",
"eslint-config-standard": "^16.0.3", "eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.23.3", "eslint-plugin-import": "^2.23.4",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0", "eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.9.0", "eslint-plugin-vue": "^7.10.0",
"sass": "^1.34.0", "sass": "^1.34.1",
"sass-loader": "^10.2.0", "sass-loader": "^10.2.0",
"standard-version": "^9.3.0", "standard-version": "^9.3.0",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.19.0",
"vue": "^2.6.12", "vue": "^2.6.14",
"vue-template-compiler": "^2.6.12", "vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0" "webpack": "^4.46.0"
} }
} }

View File

@@ -63,7 +63,9 @@ module.exports = {
functionContext: false, functionContext: false,
functionLanguage: false, functionLanguage: false,
triggerMiltipleEvents: false, triggerMiltipleEvents: false,
triggerTableInName: false,
triggerUpdateColumns: false, triggerUpdateColumns: false,
triggerOnlyRename: false,
parametersLength: false, parametersLength: false,
languages: false languages: false
}; };

View File

@@ -13,7 +13,7 @@ module.exports = {
// Structure // Structure
tables: true, tables: true,
views: true, views: true,
triggers: false, triggers: true,
routines: true, routines: true,
functions: true, functions: true,
// Settings // Settings
@@ -25,7 +25,7 @@ module.exports = {
databaseEdit: false, databaseEdit: false,
tableSettings: true, tableSettings: true,
viewSettings: true, viewSettings: true,
triggerSettings: false, triggerSettings: true,
routineSettings: true, routineSettings: true,
functionSettings: true, functionSettings: true,
indexes: true, indexes: true,
@@ -38,5 +38,8 @@ module.exports = {
functionSql: '$BODY$\r\n\r\n$BODY$', functionSql: '$BODY$\r\n\r\n$BODY$',
functionContext: true, functionContext: true,
functionLanguage: true, functionLanguage: true,
triggerMultipleEvents: true,
triggerTableInName: true,
triggerOnlyRename: true,
languages: ['sql', 'plpgsql', 'c', 'internal'] languages: ['sql', 'plpgsql', 'c', 'internal']
}; };

View File

@@ -61,6 +61,8 @@ export default (connections) => {
}); });
ipcMain.handle('update-table-cell', async (event, params) => { ipcMain.handle('update-table-cell', async (event, params) => {
delete params.row._id;
try { // TODO: move to client classes try { // TODO: move to client classes
let escapedParam; let escapedParam;
let reload = false; let reload = false;

View File

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

View File

@@ -312,6 +312,8 @@ export class MySQLClient extends AntaresCore {
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${this._schema || schema}\`.\`${table}\``); const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${this._schema || schema}\`.\`${table}\``);
const remappedFields = fields.map(row => { const remappedFields = fields.map(row => {
if (!row['Create Table']) return false;
let n = 0; let n = 0;
return row['Create Table'] return row['Create Table']
.split('') .split('')
@@ -364,7 +366,7 @@ export class MySQLClient extends AntaresCore {
return { return {
name: field.COLUMN_NAME, name: field.COLUMN_NAME,
key: field.COLUMN_KEY.toLowerCase(), key: field.COLUMN_KEY.toLowerCase(),
type: remappedFields[field.COLUMN_NAME].type, type: remappedFields ? remappedFields[field.COLUMN_NAME].type : field.DATA_TYPE,
schema: field.TABLE_SCHEMA, schema: field.TABLE_SCHEMA,
table: field.TABLE_NAME, table: field.TABLE_NAME,
numPrecision: field.NUMERIC_PRECISION, numPrecision: field.NUMERIC_PRECISION,
@@ -376,7 +378,7 @@ export class MySQLClient extends AntaresCore {
unsigned: field.COLUMN_TYPE.includes('unsigned'), unsigned: field.COLUMN_TYPE.includes('unsigned'),
zerofill: field.COLUMN_TYPE.includes('zerofill'), zerofill: field.COLUMN_TYPE.includes('zerofill'),
order: field.ORDINAL_POSITION, order: field.ORDINAL_POSITION,
default: remappedFields[field.COLUMN_NAME].default, default: remappedFields ? remappedFields[field.COLUMN_NAME].default : field.COLUMN_DEFAULT,
charset: field.CHARACTER_SET_NAME, charset: field.CHARACTER_SET_NAME,
collation: field.COLLATION_NAME, collation: field.COLLATION_NAME,
autoIncrement: field.EXTRA.includes('auto_increment'), autoIncrement: field.EXTRA.includes('auto_increment'),
@@ -582,8 +584,8 @@ export class MySQLClient extends AntaresCore {
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0], sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
name: row.Trigger, name: row.Trigger,
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0], table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0], activation: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0] event: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
}; };
})[0]; })[0];
} }
@@ -628,7 +630,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createTrigger (trigger) { async createTrigger (trigger) {
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${this._schema}\`.\`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`; const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${this._schema}\`.\`${trigger.name}\` ${trigger.activation} ${trigger.event} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -1117,7 +1119,7 @@ export class MySQLClient extends AntaresCore {
options options
} = params; } = params;
let sql = `ALTER TABLE \`${this._schema}\`.\`${table}\` `; let sql = `ALTER TABLE \`${this._schema || params.options.schema}\`.\`${table}\` `;
const alterColumns = []; const alterColumns = [];
// OPTIONS // OPTIONS
@@ -1308,7 +1310,7 @@ export class MySQLClient extends AntaresCore {
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : ''; const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
// OFFSET // OFFSET
const offsetRaw = this._query.limit.length ? `OFFSET ${this._query.offset.join(', ')} ` : ''; const offsetRaw = this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`; return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
} }

View File

@@ -496,17 +496,33 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async getTriggerInformations ({ schema, trigger }) { async getTriggerInformations ({ schema, trigger }) {
const sql = `SHOW CREATE TRIGGER \`${schema}\`.\`${trigger}\``; const [table, triggerName] = trigger.split('.');
const results = await this.raw(sql);
const results = await this.raw(`
SELECT event_object_schema AS table_schema,
event_object_table AS table_name,
trigger_schema,
trigger_name,
string_agg(event_manipulation, ',') AS event,
action_timing AS activation,
action_condition AS condition,
action_statement AS definition
FROM information_schema.triggers
WHERE trigger_schema = '${schema}'
AND trigger_name = '${triggerName}'
AND event_object_table = '${table}'
GROUP BY 1,2,3,4,6,7,8
ORDER BY table_schema,
table_name
`);
return results.rows.map(row => { return results.rows.map(row => {
return { return {
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0], sql: row.definition,
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0], name: row.trigger_name,
name: row.Trigger, table: row.table_name,
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0], event: row.event.split(','),
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0], activation: row.activation
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
}; };
})[0]; })[0];
} }
@@ -531,18 +547,21 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async alterTrigger (params) { async alterTrigger (params) {
const { trigger } = params; const { trigger } = params;
const tempTrigger = Object.assign({}, trigger); // const tempTrigger = Object.assign({}, trigger);
tempTrigger.name = `Antares_${tempTrigger.name}_tmp`; // tempTrigger.name = `Antares_${tempTrigger.name}_tmp`;
try { // try {
await this.createTrigger(tempTrigger); // await this.createTrigger(tempTrigger);
await this.dropTrigger({ trigger: tempTrigger.name }); // await this.dropTrigger({ trigger: tempTrigger.name });
await this.dropTrigger({ trigger: trigger.oldName }); // await this.dropTrigger({ trigger: trigger.oldName });
await this.createTrigger(trigger); // await this.createTrigger(trigger);
} // }
catch (err) { // catch (err) {
return Promise.reject(err); // return Promise.reject(err);
} // }
const sql = `ALTER TRIGGER ${trigger.oldName} ON ${trigger.table} RENAME TO ${trigger.name}`;
return await this.raw(sql);
} }
/** /**
@@ -552,7 +571,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTrigger (trigger) { async createTrigger (trigger) {
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`; const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event} ${trigger.activation} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -1140,7 +1159,7 @@ export class PostgreSQLClient extends AntaresCore {
const limitRaw = selectArray.length && this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : ''; const limitRaw = selectArray.length && this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
// OFFSET // OFFSET
const offsetRaw = selectArray.length && this._query.limit.length ? `OFFSET ${this._query.offset.join(', ')} ` : ''; const offsetRaw = selectArray.length && this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`; return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
} }

View File

@@ -71,11 +71,11 @@
</label> </label>
<div class="column"> <div class="column">
<div class="input-group"> <div class="input-group">
<select v-model="localTrigger.event1" class="form-select"> <select v-model="localTrigger.activation" class="form-select">
<option>BEFORE</option> <option>BEFORE</option>
<option>AFTER</option> <option>AFTER</option>
</select> </select>
<select v-model="localTrigger.event2" class="form-select"> <select v-model="localTrigger.event" class="form-select">
<option>INSERT</option> <option>INSERT</option>
<option>UPDATE</option> <option>UPDATE</option>
<option>DELETE</option> <option>DELETE</option>
@@ -106,8 +106,8 @@ export default {
sql: 'BEGIN\r\n\r\nEND', sql: 'BEGIN\r\n\r\nEND',
name: '', name: '',
table: '', table: '',
event1: 'BEFORE', activation: 'BEFORE',
event2: 'INSERT' event: 'INSERT'
}, },
isOptionsChanging: false isOptionsChanging: false
}; };

View File

@@ -37,7 +37,7 @@
> >
</div> </div>
</div> </div>
<div class="column col-auto"> <div v-if="customizations.definer" class="column col-auto">
<div class="form-group"> <div class="form-group">
<label class="form-label">{{ $t('word.definer') }}</label> <label class="form-label">{{ $t('word.definer') }}</label>
<select <select
@@ -67,7 +67,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="columns"> <fieldset class="columns" :disabled="customizations.triggerOnlyRename">
<div class="column col-auto"> <div class="column col-auto">
<div class="form-group"> <div class="form-group">
<label class="form-label">{{ $t('word.table') }}</label> <label class="form-label">{{ $t('word.table') }}</label>
@@ -82,19 +82,33 @@
<div class="form-group"> <div class="form-group">
<label class="form-label">{{ $t('word.event') }}</label> <label class="form-label">{{ $t('word.event') }}</label>
<div class="input-group"> <div class="input-group">
<select v-model="localTrigger.event1" class="form-select"> <select v-model="localTrigger.activation" class="form-select">
<option>BEFORE</option> <option>BEFORE</option>
<option>AFTER</option> <option>AFTER</option>
</select> </select>
<select v-model="localTrigger.event2" class="form-select"> <select
<option>INSERT</option> v-if="!customizations.triggerMultipleEvents"
<option>UPDATE</option> v-model="localTrigger.event"
<option>DELETE</option> class="form-select"
>
<option v-for="event in Object.keys(localEvents)" :key="event">
{{ event }}
</option>
</select> </select>
<div v-if="customizations.triggerMultipleEvents" class="px-4">
<label
v-for="event in Object.keys(localEvents)"
:key="event"
class="form-checkbox form-inline"
@change.prevent="changeEvents(event)"
>
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
</label>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </fieldset>
</div> </div>
<div class="workspace-query-results column col-12 mt-2 p-relative"> <div class="workspace-query-results column col-12 mt-2 p-relative">
<BaseLoader v-if="isLoading" /> <BaseLoader v-if="isLoading" />
@@ -136,7 +150,8 @@ export default {
localTrigger: { sql: '' }, localTrigger: { sql: '' },
lastTrigger: null, lastTrigger: null,
sqlProxy: '', sqlProxy: '',
editorHeight: 300 editorHeight: 300,
localEvents: { INSERT: false, UPDATE: false, DELETE: false }
}; };
}, },
computed: { computed: {
@@ -147,6 +162,9 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () {
return this.workspace.customizations;
},
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
}, },
@@ -209,6 +227,10 @@ export default {
async getTriggerData () { async getTriggerData () {
if (!this.trigger) return; if (!this.trigger) return;
Object.keys(this.localEvents).forEach(event => {
this.localEvents[event] = false;
});
this.localTrigger = { sql: '' }; this.localTrigger = { sql: '' };
this.isLoading = true; this.isLoading = true;
@@ -224,6 +246,12 @@ export default {
this.originalTrigger = response; this.originalTrigger = response;
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger)); this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
this.sqlProxy = this.localTrigger.sql; this.sqlProxy = this.localTrigger.sql;
if (this.customizations.triggerMultipleEvents) {
this.originalTrigger.event.forEach(e => {
this.localEvents[e] = true;
});
}
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -235,6 +263,16 @@ export default {
this.resizeQueryEditor(); this.resizeQueryEditor();
this.isLoading = false; this.isLoading = false;
}, },
changeEvents (event) {
if (this.customizations.triggerMultipleEvents) {
this.localEvents[event] = !this.localEvents[event];
this.localTrigger.event = [];
for (const key in this.localEvents) {
if (this.localEvents[key])
this.localTrigger.event.push(key);
}
}
},
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;
@@ -257,7 +295,8 @@ export default {
if (oldName !== this.localTrigger.name) { if (oldName !== this.localTrigger.name) {
this.setUnsavedChanges(false); this.setUnsavedChanges(false);
this.changeBreadcrumbs({ schema: this.schema, trigger: this.localTrigger.name }); const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
} }
this.getTriggerData(); this.getTriggerData();
@@ -274,6 +313,16 @@ export default {
clearChanges () { clearChanges () {
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger)); this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql); this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
Object.keys(this.localEvents).forEach(event => {
this.localEvents[event] = false;
});
if (this.customizations.triggerMultipleEvents) {
this.originalTrigger.event.forEach(e => {
this.localEvents[e] = true;
});
}
}, },
resizeQueryEditor () { resizeQueryEditor () {
if (this.$refs.queryEditor) { if (this.$refs.queryEditor) {

View File

@@ -346,7 +346,9 @@ export default {
closeContext () { closeContext () {
this.isContext = false; this.isContext = false;
}, },
showDeleteConfirmModal () { showDeleteConfirmModal (e) {
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
return;
this.isDeleteConfirmModal = true; this.isDeleteConfirmModal = true;
}, },
hideDeleteConfirmModal () { hideDeleteConfirmModal () {

View File

@@ -10,7 +10,7 @@
> >
<template v-if="cKey !== '_id'"> <template v-if="cKey !== '_id'">
<span <span
v-if="!isInlineEditor[cKey]" v-if="!isInlineEditor[cKey] && fields[cKey]"
class="cell-content px-2" class="cell-content px-2"
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`" :class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
@dblclick="editON($event, col, cKey)" @dblclick="editON($event, col, cKey)"
@@ -348,7 +348,7 @@ export default {
return false; return false;
}, },
enumArray () { enumArray () {
if (this.fields[this.editingField].enumValues) if (this.fields[this.editingField] && this.fields[this.editingField].enumValues)
return this.fields[this.editingField].enumValues.replaceAll('\'', '').split(','); return this.fields[this.editingField].enumValues.replaceAll('\'', '').split(',');
return false; return false;
} }

View File

@@ -95,10 +95,10 @@
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b> <i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
</div> </div>
<div v-if="results.length && results[0].rows"> <div v-if="results.length && results[0].rows">
{{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b> {{ $t('word.results') }}: <b>{{ results[0].rows.length | localeString }}</b>
</div> </div>
<div v-if="hasApproximately || page > 1"> <div v-if="hasApproximately || (page > 1 && tableInfo.rows)">
{{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small> {{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small>
</div> </div>
<div v-if="workspace.breadcrumbs.database"> <div v-if="workspace.breadcrumbs.database">
{{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b> {{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b>
@@ -157,6 +157,12 @@ export default {
ModalNewTableRow, ModalNewTableRow,
ModalFakerRows ModalFakerRows
}, },
filters: {
localeString (val) {
if (val)
return val.toLocaleString();
}
},
mixins: [tableTabs], mixins: [tableTabs],
props: { props: {
connection: Object, connection: Object,
@@ -216,6 +222,7 @@ export default {
watch: { watch: {
table () { table () {
if (this.isSelected) { if (this.isSelected) {
this.page = 1;
this.sortParams = {}; this.sortParams = {};
this.getTableData(); this.getTableData();
this.lastTable = this.table; this.lastTable = this.table;

379
src/renderer/i18n/de-DE.js Normal file
View File

@@ -0,0 +1,379 @@
module.exports = {
word: {
edit: 'Bearbeiten',
save: 'Speichern',
close: 'Schließen',
delete: 'Löschen',
confirm: 'Bestätigen',
cancel: 'Abbrechen',
send: 'Senden',
connectionName: 'Verbindungsname',
client: 'Client',
hostName: 'Hostname',
port: 'Port',
user: 'Nutzer',
password: 'Kennwort',
credentials: 'Zugangsdaten',
connect: 'Verbinden',
connected: 'Verbunden',
disconnect: 'Trennen',
disconnected: 'Getrennt',
refresh: 'Aktualisieren',
settings: 'Einstellungen',
general: 'Allgemein',
themes: 'Designs',
update: 'Aktualisierung',
about: 'Über',
language: 'Sprache',
version: 'Version',
donate: 'Spenden',
run: 'Ausführen',
schema: 'Schema',
results: 'Ergebnisse',
size: 'Größe',
seconds: 'Sekunden',
type: 'Typ',
mimeType: 'Mime-Type',
download: 'Herunterladen',
add: 'Hinzufügen',
data: 'Daten',
properties: 'Eigenschaften',
insert: 'Einfügen',
connecting: 'Verbinden',
name: 'Name',
collation: 'Kollation',
clear: 'Leeren',
options: 'Optionen',
autoRefresh: 'Auto-Aktualisierung',
indexes: 'Indizes',
foreignKeys: 'Fremdschlüssel',
length: 'Länge',
unsigned: 'Unsigniert',
default: 'Standard',
comment: 'Kommentar',
key: 'Schlüssel',
order: 'Sortierung',
expression: 'Ausdruck',
autoIncrement: 'Auto Inkrement',
engine: 'Engine',
field: 'Feld | Felder',
approximately: 'Ungefähr',
total: 'Gesamt',
table: 'Tabelle',
discard: 'Verwerfen',
stay: 'Warten',
author: 'Autor',
light: 'Hell',
dark: 'Dunkel',
autoCompletion: 'Auto-Vervollständigung',
application: 'Anwendung',
editor: 'Editor',
view: 'Ansicht',
definer: 'Bestimmer',
algorithm: 'Algorithmus',
trigger: 'Auslöser',
storedRoutine: 'Gespeicherte Routine | Gespeicherte Routinen',
scheduler: 'Zeitplaner',
event: 'Ereignis',
parameters: 'Parameter',
function: 'Funktion | Funktionen',
deterministic: 'Deterministisch',
context: 'Kontext',
export: 'Export',
returns: 'Rückgabe',
timing: 'Dauer',
state: 'Stand',
execution: 'Ausführung',
starts: 'Startet',
ends: 'Endet',
ssl: 'SSL',
privateKey: 'Privater Schlüssel',
certificate: 'Zertifikat',
caCertificate: 'CA Zertifikat',
ciphers: 'Chiffren',
upload: 'Hochladen',
browse: 'Suchen',
faker: 'Faker',
content: 'Inhalt',
cut: 'Ausschneiden',
copy: 'Kopieren',
paste: 'Einfügen',
tools: 'Tools',
variables: 'Variablen',
processes: 'Prozesse',
database: 'Datenbank',
scratchpad: 'Scratchpad',
array: 'Array',
format: 'Formatierung'
},
message: {
appWelcome: 'Willkommen im Antares SQL Client!',
appFirstStep: 'Dein erster Schritt: Erstelle eine neue Datenbankverbindung.',
addConnection: 'Verbindung hinzufügen',
createConnection: 'Verbindung erstellen',
createNewConnection: 'Neue Verbindung erstellen',
askCredentials: 'Frage nach Zugangsdaten',
testConnection: 'Verbindung testen',
editConnection: 'Verbindung bearbeiten',
deleteConnection: 'Verbindung löschen',
deleteCorfirm: 'Bestätige den Abbruch von',
connectionSuccessfullyMade: 'Verbindung erfolgreich erstellt!',
madeWithJS: 'Mit 💛 und JavaScript gemacht!',
checkForUpdates: 'Suche nach Aktualisierungen',
noUpdatesAvailable: 'Keine Aktualisierungen verfügbar',
checkingForUpdate: 'Suche nach Aktualisierungen',
checkFailure: 'Suche fehlgeschlagen, bitte versuche es später noch einmal',
updateAvailable: 'Aktualisierung verfügbar',
downloadingUpdate: 'Aktualisierung wird heruntergeladen',
updateDownloaded: 'Aktualisierung heruntergeladen',
restartToInstall: 'Starte Antares neu für die Installation',
unableEditFieldWithoutPrimary: 'Feld kann ohne Primärschlüssel in Ergebnisliste nicht bearbeitet werden',
editCell: 'Zelle bearbeiten',
deleteRows: 'Zeile löschen | Lösche {count} Zeilen',
confirmToDeleteRows: 'Eine Zeile wirklich löschen? | {count} Zeilen wirklich löschen?',
notificationsTimeout: 'Timeout für Benachrichtigungen',
uploadFile: 'Datei hochladen',
addNewRow: 'Neue Zeile hinzufügen',
numberOfInserts: 'Anzahl der eingefügten Zeilen',
openNewTab: 'Öffne einen neuen Tab',
affectedRows: 'Betroffene Zeilen',
createNewDatabase: 'Erstelle ein neue Datenbank',
databaseName: 'Datenbankname',
serverDefault: 'Server default',
deleteDatabase: 'Datenbank löschen',
editDatabase: 'Datenbank bearbeiten',
clearChanges: 'Änderungen leeren',
addNewField: 'Neues Feld hinzufügen',
manageIndexes: 'Indizes verwalten',
manageForeignKeys: 'Fremdschlüssel verwalten',
allowNull: 'Erlaube NULL',
zeroFill: 'Nullauffüllung',
customValue: 'Spezifischer Wert',
onUpdate: 'Bei Aktualisierung',
deleteField: 'Feld löschen',
createNewIndex: 'Neuen Index erstellen',
addToIndex: 'Zum Index hinzufügen',
createNewTable: 'Neue Tabelle erstellen',
emptyTable: 'Tabelle leeren',
deleteTable: 'Tabelle löschen',
emptyCorfirm: 'Wirklich leeren?',
unsavedChanges: 'Ungespeicherte Änderungen',
discardUnsavedChanges: 'Du hast ungespeicherte Änderungen. Wenn du den Tab verlässt, werden diese Änderungen verworfen.',
thereAreNoIndexes: 'Es gibt keine Indizes',
thereAreNoForeign: 'Es gibt keine Fremdschlüssel',
createNewForeign: 'Neuen Fremdschlüssel erstellen',
referenceTable: 'Referenztabelle',
referenceField: 'Referenzfeld',
foreignFields: 'Fremdfelder',
invalidDefault: 'Ungültiger Standard',
onDelete: 'Bei Löschung',
applicationTheme: 'Anwendungsdesign',
editorTheme: 'Editordesign',
wrapLongLines: 'Lange Zeilen umbrechen',
selectStatement: 'Select-Anweisung',
triggerStatement: 'Trigger-Anweisung',
sqlSecurity: 'SQL-Sicherheit',
updateOption: 'Aktualisierungsoption',
deleteView: 'View löschen',
createNewView: 'Neue View erstellen',
deleteTrigger: 'Trigger löschen',
createNewTrigger: 'Neuen Trigger erstellen',
currentUser: 'Aktueller Nutzer',
routineBody: 'Routineninhalt',
dataAccess: 'Datenzugriff',
thereAreNoParameters: 'Es gibt keine Parameter',
createNewParameter: 'Neue Parameter erstellen',
createNewRoutine: 'Neue Routine erstellen',
deleteRoutine: 'Routine löschen',
functionBody: 'Funktionsinhalt',
createNewFunction: 'Neue Funktion erstellen',
deleteFunction: 'Funktion löschen',
schedulerBody: 'Zeitplaner-Inhalt',
createNewScheduler: 'Neuen Zeitplaner erstellen',
deleteScheduler: 'Zeitplaner löschen',
preserveOnCompletion: 'Bei Vervollständigung erhalten',
enableSsl: 'Aktiviere SSL',
manualValue: 'Manueller Wert',
tableFiller: 'Tabellenfüller',
fakeDataLanguage: 'Fingierte Datensprache',
searchForElements: 'Suche nach Elemente',
selectAll: 'Alle auswählen',
queryDuration: 'Dauer der Abfrage',
includeBetaUpdates: 'Beta-Aktualisierungen berücksichtigen',
setNull: 'Setze NULL',
processesList: 'Prozessliste',
processInfo: 'Prozessinformationen',
manageUsers: 'Benutzer verwalten',
createNewSchema: 'Neues Schema erstellen',
schemaName: 'Schemaname',
editSchema: 'Schema bearbeiten',
deleteSchema: 'Schema löschen',
markdownSupported: 'Unterstützt Markdown',
plantATree: 'Pflanze einen Baum',
dataTabPageSize: 'Einträge pro Tab / Seite'
},
faker: {
address: 'Adresse',
commerce: 'Handel',
company: 'Firma',
database: 'Datenbank',
date: 'Datum',
finance: 'Finanzen',
git: 'Git',
hacker: 'Hacker',
internet: 'Internet',
lorem: 'Lorem',
name: 'Name',
music: 'Musik',
phone: 'Telefon',
random: 'Zufällig',
system: 'System',
time: 'Zeit',
vehicle: 'Fahrzeug',
zipCode: 'Postleitzahl',
zipCodeByState: 'Postleitzahl nach Stadt',
city: 'Stadt',
cityPrefix: 'Stadtpräfix',
citySuffix: 'Stadtzusatz',
streetName: 'Straßenname',
streetAddress: 'Anschrift',
streetSuffix: 'Straßenzusatz',
streetPrefix: 'Straßenpräfix',
secondaryAddress: 'Zweite Anschrift',
county: 'Landkreis',
country: 'Land',
countryCode: 'Ländercode',
state: 'Bundesland',
stateAbbr: 'Bundeslandkürzel',
latitude: 'Breitengrad',
longitude: 'Längengrad',
direction: 'Richtung',
cardinalDirection: 'Himmelsrichtung',
ordinalDirection: 'Nebenhimmelsrichtung',
nearbyGPSCoordinate: 'Nächste GPS-Koordinate',
timeZone: 'Zeitzone',
color: 'Farbe',
department: 'Abteilung',
productName: 'Produktname',
price: 'Preis',
productAdjective: 'Produkteigenschaft',
productMaterial: 'Produktmaterial',
product: 'Produkt',
productDescription: 'Produktbeschreibung',
suffixes: 'Zusätze',
companyName: 'Firmenname',
companySuffix: 'Firmenzusatz',
catchPhrase: 'Slogan',
bs: 'BS',
catchPhraseAdjective: 'Sloganeigenschaft',
catchPhraseDescriptor: 'Sloganbeschreibung',
catchPhraseNoun: 'Slogannomen',
bsAdjective: 'BS-Eigenschaft',
bsBuzz: 'BS-Klatsch',
bsNoun: 'BS-Nomen',
column: 'Spalte',
type: 'Typ',
collation: 'Kollation',
engine: 'Engine',
past: 'Vergangenheit',
future: 'Zukunft',
between: 'Zwischen',
recent: 'Kürzlich',
soon: 'Bald',
month: 'Monat',
weekday: 'Wochentag',
account: 'Konto',
accountName: 'Kontoname',
routingNumber: 'Bankleitzahl',
mask: 'Maske',
amount: 'Wert',
transactionType: 'Vorgangstyp',
currencyCode: 'Währungscode',
currencyName: 'Währungsname',
currencySymbol: 'Währungssymbol',
bitcoinAddress: 'Bitcoin-Adresse',
litecoinAddress: 'Litecoin-Adresse',
creditCardNumber: 'Kreditkartennummer',
creditCardCVV: 'Kartenprüfnummer',
ethereumAddress: 'Ethereum-Adresse',
iban: 'IBAN',
bic: 'BIC',
transactionDescription: 'Vorgangsbeschreibung',
branch: 'Zweig',
commitEntry: 'Commit-Eintrag',
commitMessage: 'Commit-Nachricht',
commitSha: 'Commit-SHA',
shortSha: 'Kurzer SHA',
abbreviation: 'Kürzel',
adjective: 'Adjektiv',
noun: 'Nomen',
verb: 'Verb',
ingverb: 'Ingverb',
phrase: 'Phrase',
avatar: 'Avatar',
email: 'E-Mail',
exampleEmail: 'Beispiel-E-Mail',
userName: 'Benutzername',
protocol: 'Protokoll',
url: 'Url',
domainName: 'Domainname',
domainSuffix: 'Domainzusatz',
domainWord: 'Domainwort',
ip: 'IP',
ipv6: 'IPv6',
userAgent: 'Browserkennung',
mac: 'Mac',
password: 'Kennwort',
word: 'Wort',
words: 'Wörter',
sentence: 'Satz',
slug: 'Slug',
sentences: 'Sätze',
paragraph: 'Paragraph',
paragraphs: 'Paragraphen',
text: 'Text',
lines: 'Zeilen',
genre: 'Genre',
firstName: 'Vorname',
lastName: 'Nachname',
middleName: 'Zweitname',
findName: 'Vollständiger Name',
jobTitle: 'Berufsbezeichnung',
gender: 'Geschlecht',
prefix: 'Präfix',
suffix: 'Zusatz',
title: 'Titel',
jobDescriptor: 'Berufsbeschreibung',
jobArea: 'Berufsfeld',
jobType: 'Anstellungsart',
phoneNumber: 'Telefonnummer',
phoneNumberFormat: 'Telefonnummerformat',
phoneFormats: 'Telefonnummerformate',
number: 'Nummer',
float: 'Gleitkommazahl',
arrayElement: 'Array-Element',
arrayElements: 'Array-Elemente',
objectElement: 'Object-Element',
uuid: 'Uuid',
boolean: 'Boolean',
image: 'Grafik',
locale: 'Sprachumgebung',
alpha: 'Alpha',
alphaNumeric: 'Alphanumerisch',
hexaDecimal: 'Hexadezimal',
fileName: 'Dateiname',
commonFileName: 'Allgemeiner Dateiname',
mimeType: 'Mimetype',
commonFileType: 'Allgemeiner Dateityp',
commonFileExt: 'Allgemeine Dateiendung',
fileType: 'Dateityp',
fileExt: 'Dateiendung',
directoryPath: 'Verzeichnispfad',
filePath: 'Dateipfad',
semver: 'Semver',
manufacturer: 'Hersteller',
model: 'Modell',
fuel: 'Treibstoff',
vin: 'Wein'
}
};

View File

@@ -10,7 +10,8 @@ const i18n = new VueI18n({
'ar-SA': require('./ar-SA'), 'ar-SA': require('./ar-SA'),
'es-ES': require('./es-ES'), 'es-ES': require('./es-ES'),
'fr-FR': require('./fr-FR'), 'fr-FR': require('./fr-FR'),
'pt-BR': require('./pt-BR') 'pt-BR': require('./pt-BR'),
'de-DE': require('./de-DE')
} }
}); });
export default i18n; export default i18n;

View File

@@ -4,5 +4,6 @@ export default {
'ar-SA': 'العربية', 'ar-SA': 'العربية',
'es-ES': 'Español', 'es-ES': 'Español',
'fr-FR': 'Français', 'fr-FR': 'Français',
'pt-BR': 'Português (Brasil)' 'pt-BR': 'Português (Brasil)',
'de-DE': 'Deutsch (Deutschland)'
}; };

View File

@@ -19,7 +19,6 @@ export default class {
} }
static updateTableCell (params) { static updateTableCell (params) {
delete params.row._id;
return ipcRenderer.invoke('update-table-cell', params); return ipcRenderer.invoke('update-table-cell', params);
} }