diff --git a/src/main/ipc-handlers/schema.js b/src/main/ipc-handlers/schema.js index 7380823e..207a8fd7 100644 --- a/src/main/ipc-handlers/schema.js +++ b/src/main/ipc-handlers/schema.js @@ -167,14 +167,12 @@ export default connections => { } }); - ipcMain.handle('export', async (event, { uid, ...rest }) => { + ipcMain.handle('export', async (event, { uid, type, tables, ...rest }) => { if (exporter !== null) return; - const type = connections[uid]._client; - switch (type) { case 'mysql': - exporter = new MysqlExporter(connections[uid], rest); + exporter = new MysqlExporter(connections[uid], tables, rest); break; default: return { diff --git a/src/main/libs/exporters/BaseExporter.js b/src/main/libs/exporters/BaseExporter.js index fe5e5970..fa5ce605 100644 --- a/src/main/libs/exporters/BaseExporter.js +++ b/src/main/libs/exporters/BaseExporter.js @@ -3,8 +3,9 @@ import path from 'path'; import EventEmitter from 'events'; export class BaseExporter extends EventEmitter { - constructor (options) { + constructor (tables, options) { super(); + this._tables = tables; this._options = options; this._isCancelled = false; this._outputStream = fs.createWriteStream(this._options.outputFile, { diff --git a/src/main/libs/exporters/sql/MysqlExporter.js b/src/main/libs/exporters/sql/MysqlExporter.js index 875cd0b7..49fd5a38 100644 --- a/src/main/libs/exporters/sql/MysqlExporter.js +++ b/src/main/libs/exporters/sql/MysqlExporter.js @@ -13,28 +13,36 @@ export default class MysqlExporter extends SqlExporter { /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; SET NAMES utf8mb4; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;`; return dump; } async getFooter () { + const footer = await super.getFooter(); + return `/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;`; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; + +${footer} +`; } async getCreateTable (tableName) { - const { rows } = await this._client.raw(`SHOW CREATE TABLE \`${this.schemaName}\`.\`${tableName}\``); + const { rows } = await this._client.raw( + `SHOW CREATE TABLE \`${this.schemaName}\`.\`${tableName}\`` + ); - if (rows.length !== 1) - return ''; + if (rows.length !== 1) return ''; - return rows[0]['Create Table'] + ';'; + const col = 'Create View' in rows[0] ? 'Create View' : 'Create Table'; + + return rows[0][col] + ';'; } getDropTable (tableName) { @@ -45,54 +53,82 @@ SET NAMES utf8mb4; let rowCount = 0; let sqlStr = ''; - const countResults = await this._client.raw(`SELECT COUNT(1) as count FROM \`${this.schemaName}\`.\`${tableName}\``); - if (countResults.rows.length === 1) - rowCount = countResults.rows[0].count; + const countResults = await this._client.raw( + `SELECT COUNT(1) as count FROM \`${this.schemaName}\`.\`${tableName}\`` + ); + if (countResults.rows.length === 1) rowCount = countResults.rows[0].count; if (rowCount > 0) { - const columns = await this._client.getTableColumns({ table: tableName, schema: this.schemaName }); + let queryLength = 0; + let rowsWritten = 0; + const { sqlInsertDivider, sqlInsertAfter } = this._options; + const columns = await this._client.getTableColumns({ + table: tableName, + schema: this.schemaName + }); const columnNames = columns.map(col => '`' + col.name + '`'); - const insertStmt = `INSERT INTO \`${tableName}\` (${columnNames.join(', ')}) VALUES`; + const insertStmt = `INSERT INTO \`${tableName}\` (${columnNames.join( + ', ' + )}) VALUES`; - const tableResult = await this._client.raw(`SELECT ${columnNames.join(', ')} FROM \`${this.schemaName}\`.\`${tableName}\``); + const tableResult = await this._client.raw( + `SELECT ${columnNames.join(', ')} FROM \`${ + this.schemaName + }\`.\`${tableName}\`` + ); sqlStr += `LOCK TABLES \`${tableName}\` WRITE;\n`; sqlStr += `/*!40000 ALTER TABLE \`${tableName}\` DISABLE KEYS */;`; sqlStr += '\n\n'; sqlStr += insertStmt; - sqlStr += '\n'; - for (const row of tableResult.rows) { - sqlStr += '\t('; + for (const rowIndex in tableResult.rows) { + const row = tableResult.rows[rowIndex]; + let sqlInsertString = ''; + + if ( + (sqlInsertDivider === 'bytes' && + queryLength >= sqlInsertAfter * 1024) || + (sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter) + ) { + sqlInsertString += `;\n${insertStmt}\n\t(`; + + queryLength = 0; + rowsWritten = 0; + } + else if (parseInt(rowIndex) === 0) sqlInsertString += '\n\t('; + else sqlInsertString += ',\n\t('; for (const i in columns) { const column = columns[i]; const val = row[column.name]; - if (val === null) - sqlStr += 'NULL'; - - else if (BIT.includes(column.type)) - sqlStr += `b'${hexToBinary(Buffer.from(val).toString('hex'))}'`; - + if (val === null) sqlInsertString += 'NULL'; + else if (BIT.includes(column.type)) { + sqlInsertString += `b'${hexToBinary( + Buffer.from(val).toString('hex') + )}'`; + } else if (BLOB.includes(column.type)) - sqlStr += `X'${val.toString('hex').toUpperCase()}'`; + sqlInsertString += `X'${val.toString('hex').toUpperCase()}'`; + else if (val === '') sqlInsertString += '\'\''; + else { + sqlInsertString += + typeof val === 'string' ? this.escapeAndQuote(val) : val; + } - else if (val === '') - sqlStr += '\'\''; - - else - sqlStr += typeof val === 'string' ? this.escapeAndQuote(val) : val; - - if (parseInt(i) !== columns.length - 1) - sqlStr += ', '; + if (parseInt(i) !== columns.length - 1) sqlInsertString += ', '; } - sqlStr += '),\n'; + sqlInsertString += ')'; + sqlStr += sqlInsertString; + + queryLength += sqlInsertString.length; + rowsWritten++; } - sqlStr += '\n'; + sqlStr += ';\n\n'; sqlStr += `/*!40000 ALTER TABLE \`${tableName}\` ENABLE KEYS */;\n`; sqlStr += 'UNLOCK TABLES;'; @@ -101,6 +137,173 @@ SET NAMES utf8mb4; return sqlStr; } + async getViews () { + const { rows: views } = await this._client.raw( + `SHOW TABLE STATUS FROM \`${this.schemaName}\` WHERE Comment = 'VIEW'` + ); + let sqlString = ''; + + for (const view of views) { + sqlString += `DROP VIEW IF EXISTS \`${view.Name}\`;\n`; + const viewSyntax = await this.getCreateTable(view.Name); + sqlString += viewSyntax.replaceAll('`' + this.schemaName + '`.', ''); + sqlString += '\n'; + } + + return sqlString; + } + + async getTriggers () { + const { rows: triggers } = await this._client.raw( + `SHOW TRIGGERS FROM \`${this.schemaName}\`` + ); + const generatedTables = this._tables + .filter(t => t.includeStructure) + .map(t => t.table); + + let sqlString = ''; + + for (const trigger of triggers) { + const { + Trigger: name, + Timing: timing, + Event: event, + Table: table, + Statement: statement, + sql_mode: sqlMode + } = trigger; + + if (!generatedTables.includes(table)) continue; + + const definer = this.getEscapedDefiner(trigger.Definer); + sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n'; + sqlString += `/*!50003 SET SQL_MODE="${sqlMode}" */;\n`; + sqlString += 'DELIMITER ;;\n'; + sqlString += '/*!50003 CREATE*/ '; + sqlString += `/*!50017 DEFINER=${definer}*/ `; + sqlString += `/*!50003 TRIGGER \`${name}\` ${timing} ${event} ON ${table} FOR EACH ROW ${statement}*/;;\n`; + sqlString += 'DELIMITER ;\n'; + sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE */;\n\n'; + } + + return sqlString; + } + + async getSchedulers () { + const { rows: schedulers } = await this._client.raw( + `SELECT *, EVENT_SCHEMA AS \`Db\`, EVENT_NAME AS \`Name\` FROM information_schema.\`EVENTS\` WHERE EVENT_SCHEMA = '${this.schemaName}'` + ); + let sqlString = ''; + + for (const scheduler of schedulers) { + const { + EVENT_NAME: name, + SQL_MODE: sqlMode, + EVENT_TYPE: type, + INTERVAL_VALUE: intervalValue, + INTERVAL_FIELD: intervalField, + STARTS: starts, + ENDS: ends, + EXECUTE_AT: at, + ON_COMPLETION: onCompletion, + STATUS: status, + EVENT_DEFINITION: definition + } = scheduler; + + const definer = this.getEscapedDefiner(scheduler.DEFINER); + const comment = this.escapeAndQuote(scheduler.EVENT_COMMENT); + + sqlString += `/*!50106 DROP EVENT IF EXISTS \`${name}\` */;\n`; + sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n'; + sqlString += `/*!50003 SET SQL_MODE='${sqlMode}' */;\n`; + sqlString += 'DELIMITER ;;\n'; + sqlString += '/*!50106 CREATE*/ '; + sqlString += `/*!50117 DEFINER=${definer}*/ `; + sqlString += `/*!50106 EVENT \`${name}\` ON SCHEDULE `; + if (type === 'RECURRING') { + sqlString += `EVERY ${intervalValue} ${intervalField} STARTS '${starts}' `; + + if (ends) sqlString += `ENDS '${ends}' `; + } + else sqlString += `AT '${at}' `; + sqlString += `ON COMPLETION ${onCompletion} ${ + status === 'disabled' ? 'DISABLE' : 'ENABLE' + } COMMENT ${comment || '\'\''} DO ${definition}*/;;\n`; + sqlString += 'DELIMITER ;\n'; + sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE*/;;\n'; + } + + return sqlString; + } + + async getFunctions () { + const { rows: functions } = await this._client.raw( + `SHOW FUNCTION STATUS WHERE \`Db\` = '${this.schemaName}';` + ); + + let sqlString = ''; + + for (const func of functions) { + sqlString += await this.getRoutineSyntax( + func.Name, + func.Type, + func.Definer + ); + } + + return sqlString; + } + + async getRoutines () { + const { rows: routines } = await this._client.raw( + `SHOW PROCEDURE STATUS WHERE \`Db\` = '${this.schemaName}';` + ); + + let sqlString = ''; + + for (const routine of routines) { + sqlString += await this.getRoutineSyntax( + routine.Name, + routine.Type, + routine.Definer + ); + } + + return sqlString; + } + + async getRoutineSyntax (name, type, definer) { + const { rows: routines } = await this._client.raw( + `SHOW CREATE ${type} \`${this.schemaName}\`.\`${name}\`` + ); + + if (routines.length === 0) return ''; + + const routine = routines[0]; + + const { sql_mode: sqlMode, 'Create Function': createProcedure } = routine; + + const startOffset = createProcedure.indexOf(type); + const procedureBody = createProcedure.substring(startOffset); + + let sqlString = 'DELIMITER ;;\n'; + sqlString = `/*!50003 DROP ${type} IF EXISTS ${name}*/;;\n`; + sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n'; + sqlString += `/*!50003 SET SQL_MODE="${sqlMode}"*/;;\n`; + sqlString += `/*!50003 CREATE*/ /*!50020 DEFINER=${definer}*/ /*!50003 ${procedureBody}*/;;\n`; + sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE*/;;\n'; + sqlString += 'DELIMITER ;\n'; + + return sqlString; + } + + getEscapedDefiner (definer) { + return definer + .split('@') + .map(part => '`' + part + '`') + .join('@'); + } + escapeAndQuote (value) { if (!value) return null; return `'${value.replaceAll(/'/g, '\'\'')}'`; diff --git a/src/main/libs/exporters/sql/SqlExporter.js b/src/main/libs/exporters/sql/SqlExporter.js index 6d488dd5..e8aed7fd 100644 --- a/src/main/libs/exporters/sql/SqlExporter.js +++ b/src/main/libs/exporters/sql/SqlExporter.js @@ -3,8 +3,8 @@ import moment from 'moment'; import { BaseExporter } from '../BaseExporter'; export class SqlExporter extends BaseExporter { - constructor (client, options) { - super(options); + constructor (client, tables, options) { + super(tables, options); this._client = client; this._commentChar = '#'; } @@ -23,8 +23,15 @@ export class SqlExporter extends BaseExporter { } async dump () { + const { includes } = this._options; + const extraItems = Object.keys(includes).filter(key => includes[key]); + const totalTableToProcess = this._tables.filter( + t => t.includeStructure || t.includeContent || t.includeDropStatement + ).length; + const processingItemCount = totalTableToProcess + extraItems.length; + const exportState = { - totalItems: this._options.items.length, + totalItems: processingItemCount, currentItemIndex: 0, currentItem: '', op: '' @@ -34,13 +41,16 @@ export class SqlExporter extends BaseExporter { this.writeString(header); this.writeString('\n\n\n'); - for (const item of this._options.items) { + for (const item of this._tables) { // user abort operation - if (this.isCancelled) - return; + if (this.isCancelled) return; // skip item if not set to output any detail for them - if (!item.includeStructure && !item.includeContent && !item.includeDropStatement) + if ( + !item.includeStructure && + !item.includeContent && + !item.includeDropStatement + ) continue; exportState.currentItemIndex++; @@ -49,7 +59,9 @@ export class SqlExporter extends BaseExporter { this.emitUpdate(exportState); - const tableHeader = this.buildComment(`Dump of table ${item.table}\n------------------------------------------------------------`); + const tableHeader = this.buildComment( + `Dump of table ${item.table}\n------------------------------------------------------------` + ); this.writeString(tableHeader); this.writeString('\n\n'); @@ -79,12 +91,38 @@ export class SqlExporter extends BaseExporter { this.writeString('\n\n'); } + for (const item of extraItems) { + const processingMethod = `get${item.charAt(0).toUpperCase() + + item.slice(1)}`; + exportState.currentItemIndex++; + exportState.currentItem = item; + exportState.op = 'PROCESSING'; + this.emitUpdate(exportState); + + if (this[processingMethod]) { + const data = await this[processingMethod](); + if (data !== '') { + const header = + this.buildComment( + `Dump of ${item}\n------------------------------------------------------------` + ) + '\n\n'; + + this.writeString(header); + this.writeString(data); + this.writeString('\n\n'); + } + } + } + const footer = await this.getFooter(); this.writeString(footer); } buildComment (text) { - return text.split('\n').map(txt => `${this._commentChar} ${txt}`).join('\n'); + return text + .split('\n') + .map(txt => `${this._commentChar} ${txt}`) + .join('\n'); } async getSqlHeader () { @@ -105,11 +143,13 @@ Generation time: ${moment().format()} } async getFooter () { - return ''; + return this.buildComment(`Dump completed on ${moment().format()}`); } getCreateTable (tableName) { - throw new Error('Sql Exporter must implement the "getCreateTable" method'); + throw new Error( + 'Sql Exporter must implement the "getCreateTable" method' + ); } getDropTable (tableName) { @@ -117,6 +157,8 @@ Generation time: ${moment().format()} } getTableInsert (tableName) { - throw new Error('Sql Exporter must implement the "getTableInsert" method'); + throw new Error( + 'Sql Exporter must implement the "getTableInsert" method' + ); } } diff --git a/src/renderer/components/ModalExportSchema.vue b/src/renderer/components/ModalExportSchema.vue index 5ed537ff..b3aedd22 100644 --- a/src/renderer/components/ModalExportSchema.vue +++ b/src/renderer/components/ModalExportSchema.vue @@ -37,111 +37,168 @@ - -
-
- - {{ filename }} -
- -
- - -
-
-
-
-
-
-
-
- Table +
+
+
+
+ + {{ filename }} +
+ +
+ + + +
+
+
+
+
+
+
+
+ {{ $t('word.table') }} +
+
+
+
+ {{ $t('word.structure') }} +
+
+
+
+ {{ $t('word.content') }} +
+
+
+
+ {{ $t('word.drop') }} +
+
-
-
- Structure -
-
-
-
- Content -
-
-
-
- Drop + +
+
+
+ {{ item.table }} +
+
+ +
+
+ +
+
+ +
+
+
+

+ {{ $t('word.options') }} +

+ {{ $t('word.includes') }}: -
-
-
- {{ item.table }} -
-
- -
-
- -
-
- -
+ + +
+ {{ $t('message.newInserStmtEvery') }}: +
+
+
+ +
+
+
-
- -

- {{ progressPercentage }}% - {{ progressStatus }} -

-
-
@@ -151,6 +208,7 @@ import { ipcRenderer } from 'electron'; import { mapActions, mapGetters } from 'vuex'; import moment from 'moment'; +import customizations from 'common/customizations'; import Application from '@/ipc-api/Application'; import Schema from '@/ipc-api/Schema'; @@ -163,9 +221,15 @@ export default { data () { return { isExporting: false, + isRefreshing: false, progressPercentage: 0, progressStatus: '', tables: [], + options: { + includes: {}, + sqlInsertAfter: 250, + sqlInsertDivider: 'bytes' + }, basePath: '' }; }, @@ -194,7 +258,7 @@ export default { } }, async created () { - await this.refreshSchema({ uid: this.currentWorkspace.uid, schema: this.selectedSchema }); + await this.refresh(); window.addEventListener('keydown', this.onKey); this.basePath = await Application.getDownloadPathDirectory(); this.tables = this.schemaItems.map(item => ({ @@ -204,6 +268,14 @@ export default { includeDropStatement: true })); + const structure = ['views', 'triggers', 'routines', 'functions', 'schedulers', 'triggerFunctions']; + + structure.forEach(feat => { + const val = customizations[this.currentWorkspace.client][feat]; + if (val) + this.$set(this.options.includes, feat, true); + }); + ipcRenderer.on('export-progress', this.updateProgress); }, beforeDestroy () { @@ -216,18 +288,20 @@ export default { }), async startExport () { this.isExporting = true; - const { uid } = this.currentWorkspace; + const { uid, client } = this.currentWorkspace; const params = { uid, + type: client, schema: this.selectedSchema, outputFile: this.dumpFilePath, - items: [...this.tables] + tables: [...this.tables], + ...this.options }; const result = await Schema.export(params); if (result) { if (result.status === 'success') - this.progressStatus = result.response.cancelled ? 'Aborted' : 'Completed!'; + this.progressStatus = result.response.cancelled ? this.$t('word.aborted') : this.$t('word.completed'); else this.progressStatus = result.response; @@ -237,7 +311,17 @@ export default { }, updateProgress (event, state) { this.progressPercentage = Number((state.currentItemIndex / state.totalItems * 100).toFixed(1)); - this.progressStatus = state.op + ' ' + state.currentItem; + switch (state.op) { + case 'PROCESSING': + this.progressStatus = this.$t('message.processingTableExport', { table: state.currentItem }); + break; + case 'FETCH': + this.progressStatus = this.$t('message.fechingTableExport', { table: state.currentItem }); + break; + case 'WRITE': + this.progressStatus = this.$t('message.writingTableExport', { table: state.currentItem }); + break; + } }, async closeModal () { let willClose = true; @@ -261,6 +345,11 @@ export default { uncheckAllTables () { this.tables = this.tables.map(item => ({ table: item.table, includeStructure: false, includeContent: false, includeDropStatement: false })); }, + async refresh () { + this.isRefreshing = true; + await this.refreshSchema({ uid: this.currentWorkspace.uid, schema: this.selectedSchema }); + this.isRefreshing = false; + }, async openPathDialog () { const result = await Application.showOpenDialog({ properties: ['openDirectory'] }); if (result && !result.canceled) @@ -271,9 +360,19 @@ export default { diff --git a/src/renderer/i18n/en-US.js b/src/renderer/i18n/en-US.js index 1b30ddc4..1fe4213b 100644 --- a/src/renderer/i18n/en-US.js +++ b/src/renderer/i18n/en-US.js @@ -122,7 +122,17 @@ module.exports = { select: 'Select', passphrase: 'Passphrase', filter: 'Filter', - change: 'Change' + change: 'Change', + views: 'Views', + triggers: 'Triggers', + routines: 'Routines', + functions: 'Functions', + schedulers: 'Schedulers', + includes: 'Includes', + drop: 'Drop', + rows: 'Rows', + completed: 'Completed', + aborted: 'Aborted' }, message: { appWelcome: 'Welcome to Antares SQL Client!', @@ -145,10 +155,12 @@ module.exports = { downloadingUpdate: 'Downloading update', updateDownloaded: 'Update downloaded', restartToInstall: 'Restart Antares to install', - unableEditFieldWithoutPrimary: 'Unable to edit a field without a primary key in resultset', + unableEditFieldWithoutPrimary: + 'Unable to edit a field without a primary key in resultset', editCell: 'Edit cell', deleteRows: 'Delete row | Delete {count} rows', - confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?', + confirmToDeleteRows: + 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?', notificationsTimeout: 'Notifications timeout', uploadFile: 'Upload file', addNewRow: 'Add new row', @@ -176,7 +188,8 @@ module.exports = { deleteTable: 'Delete table', emptyCorfirm: 'Do you confirm to empty', unsavedChanges: 'Unsaved changes', - discardUnsavedChanges: 'You have some unsaved changes. Closing this tab these changes will be discarded.', + discardUnsavedChanges: + 'You have some unsaved changes. Closing this tab these changes will be discarded.', thereAreNoIndexes: 'There are no indexes', thereAreNoForeign: 'There are no foreign keys', createNewForeign: 'Create new foreign key', @@ -249,7 +262,11 @@ module.exports = { killProcess: 'Kill process', closeTab: 'Close tab', exportSchema: 'Export schema', - directoryPath: 'Directory path' + directoryPath: 'Directory path', + newInserStmtEvery: 'New INSERT statement every', + processingTableExport: 'Processing {table}', + fechingTableExport: 'Fetching {table} data', + writingTableExport: 'Writing {table} data' }, faker: { address: 'Address', diff --git a/src/renderer/i18n/it-IT.js b/src/renderer/i18n/it-IT.js index 6adbc630..311d26f8 100644 --- a/src/renderer/i18n/it-IT.js +++ b/src/renderer/i18n/it-IT.js @@ -122,7 +122,17 @@ module.exports = { select: 'Seleziona', passphrase: 'Passphrase', filter: 'Filtra', - change: 'Cambia' + change: 'Cambia', + views: 'Viste', + triggers: 'Trigger', + routines: 'Routine', + functions: 'Function', + schedulers: 'Scheduler', + includes: 'Includi', + drop: 'Drop', + rows: 'Righe', + completed: 'Completato', + aborted: 'Annullato' }, message: { appWelcome: 'Benvenuto in Antares SQL Client!', @@ -145,10 +155,12 @@ module.exports = { downloadingUpdate: 'Download dell\'aggiornamento', updateDownloaded: 'Aggiornamento scaricato', restartToInstall: 'Riavvia Antares per installare l\'aggiornamento', - unableEditFieldWithoutPrimary: 'Impossibile modificare il campo senza una primary key nel resultset', + unableEditFieldWithoutPrimary: + 'Impossibile modificare il campo senza una primary key nel resultset', editCell: 'Modifica cella', deleteRows: 'Elimina riga | Elimina {count} righe', - confirmToDeleteRows: 'Confermi di voler cancellare una riga? | Confermi di voler cancellare {count} righe?', + confirmToDeleteRows: + 'Confermi di voler cancellare una riga? | Confermi di voler cancellare {count} righe?', notificationsTimeout: 'Timeout Notifiche', uploadFile: 'Carica file', addNewRow: 'Aggiungi nuova riga', @@ -176,7 +188,8 @@ module.exports = { deleteTable: 'Cancella tabella', emptyCorfirm: 'Confermi di voler svuotare', unsavedChanges: 'Modifiche non salvate', - discardUnsavedChanges: 'Hai modifiche non salvate. Lasciando questa scheda le modifiche saranno scartate.', + discardUnsavedChanges: + 'Hai modifiche non salvate. Lasciando questa scheda le modifiche saranno scartate.', thereAreNoIndexes: 'Non ci sono indici', thereAreNoForeign: 'Non ci sono chiavi esterne', createNewForeign: 'Crea nuova chiave esterna', @@ -236,7 +249,11 @@ module.exports = { noSchema: 'Nessuno schema', restorePreviourSession: 'Ripristina sessione precedente', exportSchema: 'Esporta schema', - directoryPath: 'Percorso directory' + directoryPath: 'Percorso directory', + newInserStmtEvery: 'Nuova istruzione INSERT ogni', + processingTableExport: 'Processo {table}', + fechingTableExport: 'Ricavo i dati {table}', + writingTableExport: 'Scrittura dati {table}' }, faker: { address: 'Indirizzo',