Resolve schema diff dependencies by selecting the appropriate node automatically and maintain the order in the generated script. Fixes #5730

This commit is contained in:
Khushboo Vashi 2020-08-12 17:36:48 +05:30 committed by Akshay Joshi
parent 7dd313f5fc
commit 5b688cf949
5 changed files with 292 additions and 14 deletions

View File

@ -43,4 +43,5 @@ Bug fixes
| `Issue #5710 <https://redmine.postgresql.org/issues/5710>`_ - Fixed an issue when comparing the table with a trigger throwing error in schema diff.
| `Issue #5713 <https://redmine.postgresql.org/issues/5713>`_ - Corrected DROP SQL syntax for catalog.
| `Issue #5716 <https://redmine.postgresql.org/issues/5716>`_ - Fixed an issue where ajax call continues to fire even after disconnect the database server.
| `Issue #5724 <https://redmine.postgresql.org/issues/5724>`_ - Clarify some of the differences when running in server mode in the docs.
| `Issue #5724 <https://redmine.postgresql.org/issues/5724>`_ - Clarify some of the differences when running in server mode in the docs.
| `Issue #5730 <https://redmine.postgresql.org/issues/5730>`_ - Resolve schema diff dependencies by selecting the appropriate node automatically and maintain the order in the generated script.

View File

@ -344,6 +344,17 @@ def get_server(sid, did):
)
@login_required
def connect_server(sid):
# Check if server is already connected then no need to reconnect again.
driver = get_driver(PG_DEFAULT_DRIVER)
manager = driver.connection_manager(sid)
conn = manager.connection()
if conn.connected():
return make_json_response(
success=1,
info=gettext("Server connected."),
data={}
)
server = Server.query.filter_by(id=sid).first()
view = SchemaDiffRegistry.get_node_view('server')
return view.connect(server.servergroup_id, sid)

View File

@ -514,6 +514,7 @@ let SchemaDiffFooterView = Backform.Form.extend({
return this;
},
});
export {
SchemaDiffSelect2Control,
SchemaDiffHeaderView,

View File

@ -0,0 +1,237 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
function handleDependencies() {
event.stopPropagation();
let isChecked = event.target.checked || (event.target.checked === undefined &&
event.target.className && event.target.className.indexOf('unchecked') == -1);
let isHeaderSelected = event.target.id.includes('header-selector');
if (this.gridContext && this.gridContext.rowIndex && _.isUndefined(this.gridContext.row.rows)) {
// Single Row Selection
let rowData = this.grid.getData().getItem(this.gridContext.rowIndex);
this.gridContext = {};
if (rowData.status) {
let depRows = this.selectDependencies(rowData, isChecked);
this.selectedRowCount = this.grid.getSelectedRows().length;
if (isChecked && depRows.length > 0)
this.grid.setSelectedRows(depRows);
else if (!isChecked)
this.grid.setSelectedRows(this.grid.getSelectedRows().filter(x => !depRows.includes(x)));
this.ddlCompare(rowData);
}
} else if((this.gridContext && this.gridContext.row && !_.isUndefined(this.gridContext.row.rows)) ||
this.selectedRowCount != this.grid.getSelectedRows().length) {
// Group / All Rows Selection
this.selectedRowCount = this.grid.getSelectedRows().length;
if (this.gridContext.row && this.gridContext.row.__group) {
let context = this.gridContext;
this.gridContext = {};
this.selectDependenciesForGroup(isChecked, context);
} else {
this.gridContext = {};
this.selectDependenciesForAll(isChecked, isHeaderSelected);
}
}
if (this.grid.getSelectedRows().length > 0) {
this.header.$el.find('button#generate-script').removeAttr('disabled');
} else {
this.header.$el.find('button#generate-script').attr('disabled', true);
}
}
function selectDependenciesForGroup(isChecked, context) {
let self = this,
finalRows = [];
if (!isChecked) {
_.each(context.row.rows, function(row) {
if (row && row.status && row.status.toLowerCase() != 'identical' ) {
let d = self.selectDependencies(row, isChecked);
finalRows = finalRows.concat(d);
}
});
}
else {
_.each(self.grid.getSelectedRows(), function(row) {
let data = self.grid.getData().getItem(row);
if (data.status && data.status.toLowerCase() != 'identical') {
finalRows = finalRows.concat(self.selectDependencies(data, isChecked));
}
});
}
finalRows = [...new Set(finalRows)];
if (isChecked)
self.grid.setSelectedRows(finalRows);
else {
let filterRows = [];
filterRows = self.grid.getSelectedRows().filter(x => !finalRows.includes(x));
self.selectedRowCount = filterRows.length;
self.grid.setSelectedRows(filterRows);
}
}
function selectDependenciesForAll(isChecked, isHeaderSelected) {
let self = this,
finalRows = [];
if(!isChecked && isHeaderSelected) {
self.dataView.getItems().map(function(el) {
el.dependentCount = [];
el.dependLevel = 1;
});
self.selectedRowCount = 0;
return;
}
_.each(self.grid.getSelectedRows(), function(row) {
let data = self.grid.getData().getItem(row);
if (data.status) {
finalRows = finalRows.concat(self.selectDependencies(data, isChecked));
}
});
finalRows = [...new Set(finalRows)];
if (isChecked && finalRows.length > 0)
self.grid.setSelectedRows(finalRows);
else if (!isChecked) {
let filterRows = [];
filterRows = self.grid.getSelectedRows().filter(x => !finalRows.includes(x));
self.selectedRowCount = filterRows.length;
self.grid.setSelectedRows(filterRows);
}
}
function selectDependencies(data, isChecked) {
let self = this,
rows = [],
setDependencies = undefined,
setOrigDependencies = undefined,
finalRows = [];
if (!data.dependLevel || !isChecked) data.dependLevel = 1;
if (!data.dependentCount || !isChecked) data.dependentCount = [];
if (data.status && data.status.toLowerCase() == 'identical') {
self.selectedRowCount = self.grid.getSelectedRows().length;
return [];
}
setDependencies = function(rowData, dependencies, isChecked) {
_.each(dependencies, function(dependency) {
if (dependency.length == 0) return;
let dependencyData = [];
dependencyData = self.dataView.getItems().filter(item => item.type == dependency.type && item.oid == dependency.oid);
if (dependencyData.length > 0) {
dependencyData = dependencyData[0];
if (!dependencyData.dependentCount) dependencyData.dependentCount = [];
let groupData = [];
if (dependencyData.status && dependencyData.status.toLowerCase() != 'identical') {
groupData = self.dataView.getGroups().find(
(item) => { if (dependencyData.group_name == item.groupingKey) return item.groups; }
);
if (groupData && groupData.groups) {
groupData = groupData.groups.find(
(item) => { return item.groupingKey == dependencyData.group_name + ':|:' + dependencyData.type; }
);
if (groupData && groupData.collapsed == 1)
self.dataView.expandGroup(dependencyData.group_name + ':|:' + dependencyData.type);
}
if (isChecked || _.isUndefined(isChecked)) {
dependencyData.dependLevel = rowData.dependLevel + 1;
if (dependencyData.dependentCount.indexOf(rowData.oid) === -1)
dependencyData.dependentCount.push(rowData.oid);
rows[rows.length] = dependencyData;
} else {
dependencyData.dependentCount.splice(dependencyData.dependentCount.indexOf(rowData.oid), 1);
if (dependencyData.dependentCount.length == 0) {
rows[rows.length] = dependencyData;
dependencyData.dependLevel = 1;
}
}
}
if (Object.keys(dependencyData.dependencies).length > 0) {
if (dependencyData.dependentCount.indexOf(rowData.oid) !== -1 ) {
let depCirRows = dependencyData.dependencies.filter(x => x.oid !== rowData.oid);
if (!dependencyData.orig_dependencies)
dependencyData.orig_dependencies = Object.assign([], dependencyData.dependencies);
dependencyData.dependencies = depCirRows;
}
setDependencies(dependencyData, dependencyData.dependencies, isChecked);
}
}
});
};
setDependencies(data, data.dependencies, isChecked);
setOrigDependencies = function(dependencies) {
_.each(dependencies, function(dependency) {
if (dependency.length == 0) return;
let dependencyData = [];
dependencyData = self.dataView.getItems().filter(item => item.type == dependency.type && item.oid == dependency.oid);
if (dependencyData.length > 0) {
dependencyData = dependencyData[0];
if (dependencyData.orig_dependencies && Object.keys(dependencyData.orig_dependencies).length > 0) {
if (!dependencyData.dependentCount) dependencyData.dependentCount = [];
if (dependencyData.status && dependencyData.status.toLowerCase() != 'identical') {
dependencyData.dependencies = dependencyData.orig_dependencies;
dependencyData.orig_dependencies = [];
}
if (dependencyData.dependencies.length > 0) {
setOrigDependencies(dependencyData.dependencies);
}
}
}
});
};
setOrigDependencies(data.dependencies);
if (isChecked) finalRows = self.grid.getSelectedRows();
_.each(rows, function(row) {
let r = self.grid.getData().getRowByItem(row);
if(!_.isUndefined(r) && finalRows.indexOf(r) === -1 ) {
finalRows.push(self.grid.getData().getRowByItem(row));
}
});
self.selectedRowCount = finalRows.length;
return finalRows;
}
export {
handleDependencies,
selectDependenciesForGroup,
selectDependenciesForAll,
selectDependencies,
};

View File

@ -19,9 +19,12 @@ import {generateScript} from 'tools/datagrid/static/js/show_query_tool';
import 'pgadmin.sqleditor';
import pgWindow from 'sources/window';
import {SchemaDiffSelect2Control, SchemaDiffHeaderView,
import { SchemaDiffSelect2Control, SchemaDiffHeaderView,
SchemaDiffFooterView, SchemaDiffSqlControl} from './schema_diff.backform';
import { handleDependencies, selectDependenciesForGroup,
selectDependenciesForAll, selectDependencies } from './schema_diff_dependency';
var wcDocker = window.wcDocker;
export default class SchemaDiffUI {
@ -198,10 +201,11 @@ export default class SchemaDiffUI {
script_header;
script_header = gettext('-- This script was generated by a beta version of the Schema Diff utility in pgAdmin 4. \n');
script_header += gettext('-- This version does not include dependency resolution, and may require manual changes \n');
script_header += gettext('-- to the script to ensure changes are applied in the correct order.\n');
script_header += gettext('-- For the circular dependencies, the order in which Schema Diff writes the objects is not very sophisticated \n');
script_header += gettext('-- and may require manual changes to the script to ensure changes are applied in the correct order.\n');
script_header += gettext('-- Please report an issue for any failure with the reproduction steps. \n');
_.each(url_params, function(key, val) {
url_params[key] = parseInt(val, 10);
});
@ -249,14 +253,22 @@ export default class SchemaDiffUI {
};
if (sel_rows.length > 0) {
let script_body = '';
let script_array = {1: [], 2: [], 3: [], 4: [], 5: []},
script_body = '';
for (var row = 0; row < sel_rows.length; row++) {
let data = self.grid.getData().getItem(sel_rows[row]);
if(!_.isUndefined(data.diff_ddl)) {
script_body += data.diff_ddl + '\n\n';
if (!(data.dependLevel in script_array)) script_array[data.dependLevel] = [];
script_array[data.dependLevel].push(data.diff_ddl);
}
}
_.each(Object.keys(script_array).reverse(), function(s) {
if (script_array[s].length > 0) {
script_body += script_array[s].join('\n') + '\n\n';
}
});
generated_script = script_header + 'BEGIN;' + '\n' + script_body + 'END;';
open_query_tool();
} else if (!_.isUndefined(self.model.get('diff_ddl'))) {
@ -362,9 +374,10 @@ export default class SchemaDiffUI {
// Change Row css on the basis of item status
self.dataView.getItemMetadata = function(row) {
var item = self.dataView.getItem(row);
let item = self.dataView.getItem(row),
group_item = groupItemMetadataProvider.getGroupRowMetadata(item);
if (item.__group) {
return groupItemMetadataProvider.getGroupRowMetadata(item);
return group_item;
}
if(item.status === 'Different') {
@ -393,6 +406,17 @@ export default class SchemaDiffUI {
self.dataView.syncGridSelection(grid, true, true);
grid.onMouseEnter.subscribe(function (evt) {
var cell = grid.getCellFromEvent(evt);
self.gridContext = {};
self.gridContext.rowIndex = cell.row;
self.gridContext.row = grid.getDataItem(cell.row);
}.bind(self));
grid.onMouseLeave.subscribe(function () {
self.gridContext = {};
});
grid.onClick.subscribe(function(e, args) {
if (args.row) {
data = args.grid.getData().getItem(args.row);
@ -400,9 +424,9 @@ export default class SchemaDiffUI {
}
}.bind(self));
grid.onSelectedRowsChanged.subscribe(self.handle_generate_button.bind(self));
grid.onSelectedRowsChanged.subscribe(self.handleDependencies.bind(this));
self.model.on('change:diff_ddl', self.handle_generate_button.bind(self));
self.model.on('change:diff_ddl', self.handleDependencies.bind(self));
$('#schema-diff-grid').on('keyup', function() {
if ((event.keyCode == 38 || event.keyCode ==40) && this.grid.getActiveCell().row) {
@ -414,11 +438,10 @@ export default class SchemaDiffUI {
self.render_grid_data(data);
}
render_grid_data(data) {
var self = this;
self.grid.setSelectedRows([]);
self.selected_row_count = self.grid.getSelectedRows().length;
data.sort((a, b) => (a.label > b.label) ? 1 : (a.label === b.label) ? ((a.title > b.title) ? 1 : -1) : -1);
self.dataView.beginUpdate();
self.dataView.setItems(data);
@ -503,7 +526,7 @@ export default class SchemaDiffUI {
'source_ddl': undefined,
'target_ddl': undefined,
'diff_ddl': undefined,
});
}, {silent: true});
if(data.status && data.status.toLowerCase() == 'identical') {
var url_params = self.selection;
@ -541,7 +564,7 @@ export default class SchemaDiffUI {
'source_ddl': data.source_ddl,
'target_ddl': data.target_ddl,
'diff_ddl': data.diff_ddl,
});
}, {silent: true});
self.footer.render();
}
@ -855,3 +878,8 @@ export default class SchemaDiffUI {
});
}
}
SchemaDiffUI.prototype.handleDependencies = handleDependencies;
SchemaDiffUI.prototype.selectDependenciesForGroup = selectDependenciesForGroup;
SchemaDiffUI.prototype.selectDependenciesForAll = selectDependenciesForAll;
SchemaDiffUI.prototype.selectDependencies = selectDependencies;