diff --git a/e2e-tests/cypress/e2e/multi-sql.cy.js b/e2e-tests/cypress/e2e/multi-sql.cy.js index f90ca6789..1c5be63a2 100644 --- a/e2e-tests/cypress/e2e/multi-sql.cy.js +++ b/e2e-tests/cypress/e2e/multi-sql.cy.js @@ -103,13 +103,70 @@ describe('Transactions', () => { describe('Backup table', () => { multiTest({ skipMongo: true }, (connectionName, databaseName, engine, options = {}) => { + const implicitTransactions = options.implicitTransactions ?? false; + cy.contains(connectionName).click(); if (databaseName) cy.contains(databaseName).click(); - cy.contains('customers').rightclick(); + cy.contains('addresses').rightclick(); cy.contains('Create table backup').click(); cy.testid('ConfirmSqlModal_okButton').click(); - cy.contains('customers (').click(); - cy.contains('Rows: 8').should('be.visible'); + cy.testid('app-object-group-items-table-backups').contains('addresses').click(); + cy.contains('Rows: 12').should('be.visible'); + cy.testid('app-object-group-items-tables').contains('addresses').click(); + + cy.contains('Ridgewood').click(); + cy.testid('TableDataTab_deleteSelectedRows').click(); + cy.contains('Rosewood').click(); + cy.testid('TableDataTab_deleteSelectedRows').click(); + + cy.contains('Vermont').click(); + cy.get('body').realType('Wermont{enter}'); + + cy.testid('TableDataTab_insertNewRow').click(); + cy.get('body').realType('Modranska{enter}'); + cy.realPress(['ArrowLeft']); + cy.realPress(['ArrowLeft']); + cy.get('body').realType('13{enter}'); + cy.realPress(['ArrowRight']); + cy.get('body').realType('1{enter}'); + cy.realPress(['ArrowRight']); + cy.realPress(['ArrowRight']); + cy.realPress(['ArrowRight']); + cy.get('body').realType('Prague{enter}'); + cy.realPress(['ArrowRight']); + cy.get('body').realType('CZ{enter}'); + cy.realPress(['ArrowRight']); + cy.get('body').realType('10000{enter}'); + cy.realPress(['ArrowRight']); + cy.get('body').realType('111222333{enter}'); + + cy.testid('TableDataTab_save').click(); + cy.testid('ConfirmSqlModal_okButton').click(); + cy.contains('Rows: 11').should('be.visible'); // wait for save + + cy.testid('app-object-group-items-table-backups').contains('addresses').rightclick(); + cy.contains('restore script').click(); + cy.contains('UPDATE'); // wait for query + cy.testid('QueryTab_executeButton').click(); + cy.contains('Query execution finished'); + + if (implicitTransactions) { + cy.testid('QueryTab_commitTransactionButton').click(); + cy.contains('Commit Transaction finished'); + } + + cy.realPress('F1'); + cy.realType('Close all'); + cy.realPress('Enter'); + // cy.testid('CloseTabModal_buttonConfirm').click(); + cy.wait(1000); + + cy.testid('app-object-group-items-tables').contains('addresses').click(); + + // check whether data was successfully restored + cy.contains('Rows: 12').should('be.visible'); + cy.contains('Ridgewood'); + cy.contains('Vermont'); }); }); diff --git a/packages/web/src/modals/CloseTabModal.svelte b/packages/web/src/modals/CloseTabModal.svelte index 62d67f22c..eb86cc071 100644 --- a/packages/web/src/modals/CloseTabModal.svelte +++ b/packages/web/src/modals/CloseTabModal.svelte @@ -44,6 +44,7 @@ { closeCurrentModal(); onConfirm(); @@ -52,6 +53,7 @@ { closeCurrentModal(); onCancel(); diff --git a/packages/web/src/utility/tableRestoreScript.ts b/packages/web/src/utility/tableRestoreScript.ts index ae768f5ea..ec4f7479a 100644 --- a/packages/web/src/utility/tableRestoreScript.ts +++ b/packages/web/src/utility/tableRestoreScript.ts @@ -13,6 +13,10 @@ export function createTableRestoreScript(backupTable: TableInfo, originalTable: ); const valueColumns = _.difference(bothColumns, keyColumns); + if (keyColumns.length === 0) { + throw new Error('Cannot create restore script: no key columns found'); + } + function makeColumnCond(colName: string, operator: '=' | '<>' | '<' | '>' | '<=' | '>=' = '='): Condition { return { conditionType: 'binary', @@ -30,6 +34,30 @@ export function createTableRestoreScript(backupTable: TableInfo, originalTable: }; } + function makeNullNotNullCond(colName: string, isCond1: boolean): Condition { + return { + conditionType: 'and', + conditions: [ + { + conditionType: 'isNull', + expr: { + exprType: 'column', + columnName: colName, + source: isCond1 ? { name: originalTable } : { alias: 'bak' }, + }, + }, + { + conditionType: 'isNotNull', + expr: { + exprType: 'column', + columnName: colName, + source: isCond1 ? { alias: 'bak' } : { name: originalTable }, + }, + }, + ], + }; + } + function putTitle(title: string) { dmp.putRaw('\n\n'); dmp.comment(`******************** ${title} ********************`); @@ -45,50 +73,56 @@ export function createTableRestoreScript(backupTable: TableInfo, originalTable: dmp.comment(`Follows UPDATE, DELETE, INSERT statements to restore data`); dmp.putRaw('\n'); - const update: Update = { - commandType: 'update', - from: { name: originalTable }, - fields: valueColumns.map(colName => ({ - exprType: 'select', - select: { - commandType: 'select', - from: { name: backupTable, alias: 'bak' }, - columns: [ - { - exprType: 'column', - columnName: colName, - source: { alias: 'bak' }, - }, - ], - where: { - conditionType: 'and', - conditions: keyColumns.map(colName => makeColumnCond(colName)), - }, - }, - targetColumn: colName, - })), - where: { - conditionType: 'exists', - subQuery: { - commandType: 'select', - from: { name: backupTable, alias: 'bak' }, - selectAll: true, - where: { - conditionType: 'and', - conditions: [ - ...keyColumns.map(keyColName => makeColumnCond(keyColName)), + if (valueColumns.length > 0) { + const update: Update = { + commandType: 'update', + from: { name: originalTable }, + fields: valueColumns.map(colName => ({ + exprType: 'select', + select: { + commandType: 'select', + from: { name: backupTable, alias: 'bak' }, + columns: [ { - conditionType: 'or', - conditions: valueColumns.map(colName => makeColumnCond(colName, '<>')), + exprType: 'column', + columnName: colName, + source: { alias: 'bak' }, }, ], + where: { + conditionType: 'and', + conditions: keyColumns.map(colName => makeColumnCond(colName)), + }, + }, + targetColumn: colName, + })), + where: { + conditionType: 'exists', + subQuery: { + commandType: 'select', + from: { name: backupTable, alias: 'bak' }, + selectAll: true, + where: { + conditionType: 'and', + conditions: [ + ...keyColumns.map(keyColName => makeColumnCond(keyColName)), + { + conditionType: 'or', + conditions: valueColumns.flatMap(colName => [ + makeColumnCond(colName, '<>'), + makeNullNotNullCond(colName, true), + makeNullNotNullCond(colName, false), + ]), + }, + ], + }, }, }, - }, - }; - putTitle('UPDATE'); - dumpSqlUpdate(dmp, update); - dmp.endCommand(); + }; + putTitle('UPDATE'); + dumpSqlUpdate(dmp, update); + dmp.endCommand(); + } const delcmd: Delete = { commandType: 'delete', @@ -127,6 +161,14 @@ export function createTableRestoreScript(backupTable: TableInfo, originalTable: }; putTitle('INSERT'); + + const autoinc = originalTable.columns.find(x => x.autoIncrement); + if (autoinc) { + dmp.allowIdentityInsert(originalTable, true); + } dumpSqlInsert(dmp, insert); dmp.endCommand(); + if (autoinc) { + dmp.allowIdentityInsert(originalTable, false); + } }