Added options 'Ignore owner' and 'Ignore whitespace' to the schema diff panel. Fixes #7282

This commit is contained in:
Akshay Joshi 2022-04-20 15:27:17 +05:30
parent 58e4f766d8
commit 1ac13a2788
15 changed files with 149 additions and 39 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 167 KiB

View File

@ -453,7 +453,7 @@ Expand the *Schema Diff* node to specify your display preferences.
Use the *Ignore owner* switch to ignores the owner while comparing the objects.
Use the *Ignore whitespaces* switch to ignores the whitespaces while comparing
Use the *Ignore whitespace* switch to ignores the whitespace while comparing
the string objects. Whitespace includes space, tabs, and CRLF.

View File

@ -13,6 +13,7 @@ New features
| `Issue #3989 <https://redmine.postgresql.org/issues/3989>`_ - Ensure that row numbers should be visible in view when scrolling horizontally.
| `Issue #6830 <https://redmine.postgresql.org/issues/6830>`_ - Relocate GIS Viewer Button to the Left Side of the Results Table.
| `Issue #7012 <https://redmine.postgresql.org/issues/7012>`_ - Disable the master password requirement when using alternative authentication sources.
| `Issue #7282 <https://redmine.postgresql.org/issues/7282>`_ - Added options 'Ignore owner' and 'Ignore whitespace' to the schema diff panel.
Housekeeping
************

View File

@ -65,6 +65,12 @@ same or different (and within the same server or from different servers).
:alt: Schema diff compare button
:align: center
Use the drop-down near to *Compare* button to ignore owner and ignore whitespace.
* Ignore owner Select to ignores the owner while comparing the objects.
* Ignore whitespace Select to ignores the whitespace while comparing the string objects. Whitespace includes space, tabs, and CRLF.
After you select servers, and databases, click on the
*Compare* button to obtain the *Comparison Result*.

View File

@ -54,6 +54,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
target_params = {'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid')}
ignore_owner = kwargs.get('ignore_owner')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
group_name = kwargs.get('group_name')
source_schema_name = kwargs.get('source_schema_name', None)
@ -85,7 +87,9 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
node_label=self.blueprint.collection_label,
group_name=group_name,
ignore_keys=self.keys_to_ignore,
source_schema_name=source_schema_name)
source_schema_name=source_schema_name,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
def ddl_compare(self, **kwargs):
"""
@ -274,6 +278,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
source = kwargs.get('source')
target = kwargs.get('target')
diff_dict = kwargs.get('diff_dict')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
# Get the difference result for source and target columns
col_diff = self.table_col_comp(source, target)
@ -337,7 +342,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
"dict2": dict2,
"source": source,
"target": target,
"target_schema": target_schema
"target_schema": target_schema,
"ignore_whitespaces": ignore_whitespaces
}
diff = self._compare_source_and_target(
intersect_keys, module_view, source_params,
@ -385,11 +391,13 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
source = kwargs['source']
target = kwargs['target']
target_schema = kwargs['target_schema']
ignore_whitespaces = kwargs.get('ignore_whitespaces')
for key in intersect_keys:
# Recursively Compare the two dictionary
if not are_dictionaries_identical(
dict1[key], dict2[key], self.keys_to_ignore):
dict1[key], dict2[key], self.keys_to_ignore,
ignore_whitespaces):
diff_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,

View File

@ -80,21 +80,20 @@ class SchemaDiffModule(PgAdminModule):
self.preference.register(
'display', 'ignore_whitespaces',
gettext("Ignore whitespaces"), 'boolean', False,
gettext("Ignore whitespace"), 'boolean', False,
category_label=PREF_LABEL_DISPLAY,
help_str=gettext('If set to True, then the Schema Diff '
'tool ignores the whitespaces while comparing '
'the string objects. Whitespace includes space, '
'tabs, and CRLF')
help_str=gettext('Set ignore whitespace on or off by default in '
'the drop-down menu near the Compare button in '
'the Schema Diff tab.')
)
self.preference.register(
'display', 'ignore_owner',
gettext("Ignore owner"), 'boolean', False,
category_label=PREF_LABEL_DISPLAY,
help_str=gettext('If set to True, then the Schema Diff '
'tool ignores the owner while comparing '
'the objects.')
help_str=gettext('Set ignore owner on or off by default in the '
'drop-down menu near the Compare button in the '
'Schema Diff tab.')
)
@ -457,12 +456,14 @@ def schemas(sid, did):
@blueprint.route(
'/compare_database/<int:trans_id>/<int:source_sid>/<int:source_did>/'
'<int:target_sid>/<int:target_did>',
'<int:target_sid>/<int:target_did>/<int:ignore_owner>/'
'<int:ignore_whitespaces>',
methods=["GET"],
endpoint="compare_database"
)
@login_required
def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
def compare_database(trans_id, source_sid, source_did, target_sid, target_did,
ignore_owner, ignore_whitespaces):
"""
This function will compare the two databases.
"""
@ -479,6 +480,9 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
diff_model_obj)
try:
ignore_owner = bool(ignore_owner)
ignore_whitespaces = bool(ignore_whitespaces)
# Fetch all the schemas of source and target database
# Compare them and get the status.
schema_result = fetch_compare_schemas(source_sid, source_did,
@ -501,7 +505,9 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
source_sid=source_sid, source_did=source_did,
target_sid=target_sid, target_did=target_did,
diff_model_obj=diff_model_obj, total_percent=total_percent,
node_percent=node_percent)
node_percent=node_percent,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
@ -519,7 +525,9 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent,
is_schema_source_only=True)
is_schema_source_only=True,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
@ -536,7 +544,9 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
schema_name=item['schema_name'],
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent)
node_percent=node_percent,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
@ -554,7 +564,9 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
schema_name=item['schema_name'],
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent)
node_percent=node_percent,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
@ -573,13 +585,15 @@ def compare_database(trans_id, source_sid, source_did, target_sid, target_did):
@blueprint.route(
'/compare_schema/<int:trans_id>/<int:source_sid>/<int:source_did>/'
'<int:source_scid>/<int:target_sid>/<int:target_did>/<int:target_scid>',
'<int:source_scid>/<int:target_sid>/<int:target_did>/<int:target_scid>/'
'<int:ignore_owner>/<int:ignore_whitespaces>',
methods=["GET"],
endpoint="compare_schema"
)
@login_required
def compare_schema(trans_id, source_sid, source_did, source_scid,
target_sid, target_did, target_scid):
target_sid, target_did, target_scid, ignore_owner,
ignore_whitespaces):
"""
This function will compare the two schema.
"""
@ -595,6 +609,8 @@ def compare_schema(trans_id, source_sid, source_did, source_scid,
update_session_diff_transaction(trans_id, session_obj,
diff_model_obj)
try:
ignore_owner = bool(ignore_owner)
ignore_whitespaces = bool(ignore_whitespaces)
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes()
node_percent = round(100 / len(all_registered_nodes))
total_percent = 0
@ -608,7 +624,9 @@ def compare_schema(trans_id, source_sid, source_did, source_scid,
schema_name=gettext('Schema Objects'),
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent)
node_percent=node_percent,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
@ -769,6 +787,8 @@ def compare_database_objects(**kwargs):
diff_model_obj = kwargs.get('diff_model_obj')
total_percent = kwargs.get('total_percent')
node_percent = kwargs.get('node_percent')
ignore_owner = kwargs.get('ignore_owner')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
comparison_result = []
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes(None,
@ -788,7 +808,9 @@ def compare_database_objects(**kwargs):
source_did=source_did,
target_sid=target_sid,
target_did=target_did,
group_name=gettext('Database Objects'))
group_name=gettext('Database Objects'),
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
if res is not None:
comparison_result = comparison_result + res
@ -817,6 +839,9 @@ def compare_schema_objects(**kwargs):
total_percent = kwargs.get('total_percent')
node_percent = kwargs.get('node_percent')
is_schema_source_only = kwargs.get('is_schema_source_only', False)
ignore_owner = kwargs.get('ignore_owner')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
source_schema_name = None
if is_schema_source_only:
driver = get_driver(PG_DEFAULT_DRIVER)
@ -848,7 +873,9 @@ def compare_schema_objects(**kwargs):
target_did=target_did,
target_scid=target_scid,
group_name=gettext(schema_name),
source_schema_name=source_schema_name)
source_schema_name=source_schema_name,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
if res is not None:
comparison_result = comparison_result + res

View File

@ -57,6 +57,8 @@ class SchemaDiffObjectCompare:
'did': kwargs.get('source_did')}
target_params = {'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did')}
ignore_owner = kwargs.get('ignore_owner', False)
ignore_whitespaces = kwargs.get('ignore_whitespaces', False)
group_name = kwargs.get('group_name')
source_schema_name = kwargs.get('source_schema_name', None)
@ -97,7 +99,9 @@ class SchemaDiffObjectCompare:
node_label=self.blueprint.collection_label,
group_name=group_name,
ignore_keys=self.keys_to_ignore,
source_schema_name=source_schema_name)
source_schema_name=source_schema_name,
ignore_owner=ignore_owner,
ignore_whitespaces=ignore_whitespaces)
def ddl_compare(self, **kwargs):
"""

View File

@ -253,6 +253,7 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
target_params = kwargs['target_params']
group_name = kwargs['group_name']
target_schema = kwargs.get('target_schema')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
for key in intersect_keys:
source_object_id, target_object_id = \
get_source_target_oid(source_dict, target_dict, key)
@ -263,7 +264,8 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
current_app.logger.debug(
"Schema Diff: Target Dict: {0}".format(dict2[key]))
if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys):
if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys,
ignore_whitespaces):
title = key
if node == 'user_mapping':
title = _get_user_mapping_name(key)
@ -316,7 +318,8 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
source_params=temp_src_params,
target_params=temp_tgt_params,
source=dict1[key], target=dict2[key], diff_dict=diff_dict,
target_schema=target_schema)
target_schema=target_schema,
ignore_whitespaces=ignore_whitespaces)
else:
temp_src_params = copy.deepcopy(source_params)
temp_tgt_params = copy.deepcopy(target_params)
@ -383,6 +386,8 @@ def compare_dictionaries(**kwargs):
node_label = kwargs.get('node_label')
ignore_keys = kwargs.get('ignore_keys', None)
source_schema_name = kwargs.get('source_schema_name')
ignore_owner = kwargs.get('ignore_owner')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
dict1 = copy.deepcopy(source_dict)
dict2 = copy.deepcopy(target_dict)
@ -413,9 +418,7 @@ def compare_dictionaries(**kwargs):
target_only = _get_target_list(removed, target_dict, node, target_params,
view_object, node_label, group_name)
pref = Preferences.module('schema_diff')
ignore_owner = pref.preference('ignore_owner').get()
# if ignore_owner if True then add all the possible owner keys to the
# if ignore_owner is True then add all the possible owner keys to the
# ignore keys.
if ignore_owner:
owner_keys = ['owner', 'eventowner', 'funcowner', 'fdwowner',
@ -431,7 +434,8 @@ def compare_dictionaries(**kwargs):
"source_params": source_params,
"target_params": target_params,
"group_name": group_name,
"target_schema": target_schema
"target_schema": target_schema,
"ignore_whitespaces": ignore_whitespaces
}
identical, different = _get_identical_and_different_list(
@ -441,12 +445,14 @@ def compare_dictionaries(**kwargs):
return source_only + target_only + different + identical
def are_lists_identical(source_list, target_list, ignore_keys):
def are_lists_identical(source_list, target_list, ignore_keys,
ignore_whitespaces):
"""
This function is used to compare two list.
:param source_list:
:param target_list:
:param ignore_keys: ignore keys to compare
:param ignore_whitespaces:
:return:
"""
if source_list is None or target_list is None or \
@ -459,7 +465,8 @@ def are_lists_identical(source_list, target_list, ignore_keys):
if isinstance(source_list[index], dict):
if not are_dictionaries_identical(source_list[index],
target_list[index],
ignore_keys):
ignore_keys,
ignore_whitespaces):
return False
else:
if source_list[index] != target_list[index]:
@ -467,18 +474,17 @@ def are_lists_identical(source_list, target_list, ignore_keys):
return True
def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
def are_dictionaries_identical(source_dict, target_dict, ignore_keys,
ignore_whitespaces):
"""
This function is used to recursively compare two dictionaries with
same keys.
:param source_dict: source dict
:param target_dict: target dict
:param ignore_keys: ignore keys to compare
:param ignore_whitespaces: ignore whitespaces while comparing
:return:
"""
pref = Preferences.module('schema_diff')
ignore_whitespaces = pref.preference('ignore_whitespaces').get()
src_keys = set(source_dict.keys())
tar_keys = set(target_dict.keys())
@ -511,7 +517,8 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
if isinstance(source_dict[key], dict):
if not are_dictionaries_identical(source_dict[key],
target_dict[key],
ignore_keys):
ignore_keys,
ignore_whitespaces):
return False
elif isinstance(source_dict[key], list):
# Sort the source and target list on the basis of
@ -520,7 +527,7 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
target_dict[key])
# Compare the source and target lists
if not are_lists_identical(source_dict[key], target_dict[key],
ignore_keys):
ignore_keys, ignore_whitespaces):
return False
else:
source_value = source_dict[key]

View File

@ -153,3 +153,9 @@
.slick-cell .ml-3 {
margin-left: 3rem !important;
}
#btn-ignore-dropdown {
color: #fff !important;
background-color: #326690 !important;
border-color: #326690 !important;
}

View File

@ -317,7 +317,29 @@ let SchemaDiffHeaderView = Backform.Form.extend({
<div class="col-6 target row"></div>
<div class="col-5 target-buttons">
<div class="action-btns d-flex">
<button class="btn btn-primary mr-auto"><span class="pg-font-icon icon-compare sql-icon-lg"></span>&nbsp;` + gettext('Compare') + `</button>
<div class="btn-group mr-auto" role="group" aria-label="">
<button class="btn btn-primary"><span class="pg-font-icon icon-compare sql-icon-lg"></span>&nbsp;` + gettext('Compare') + `</button>
<button id="btn-ignore-dropdown" type="button" class="btn btn-primary-icon dropdown-toggle dropdown-toggle-split mr-auto"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="ignore"
title=""
tabindex="0">
</button>` +
[
'<ul class="dropdown-menu ignore">',
'<li>',
'<a class="dropdown-item" id="btn-ignore-owner" href="#" tabindex="0">',
'<i class="fa fa-check visibility-hidden" aria-hidden="true"></i>',
'<span> ' + gettext('Ignore owner') + ' </span>',
'</a>',
'</li>',
'<li>',
'<a class="dropdown-item" id="btn-ignore-whitespaces" href="#" tabindex="0">',
'<i class="fa fa-check visibility-hidden" aria-hidden="true"></i>',
'<span> ' + gettext('Ignore whitespace') + ' </span>',
'</a>',
'</li>',
'</ul>',
].join('\n') + `</div>
<button id="generate-script" class="btn btn-primary-icon mr-1" disabled><i class="fa fa-file-code sql-icon-lg"></i>&nbsp;` + gettext('Generate Script') + `</button>
<div class="btn-group mr-1" role="group" aria-label="">
<button id="btn-filter" type="button" class="btn btn-primary-icon"

View File

@ -38,6 +38,9 @@ export default class SchemaDiffUI {
this.trans_id = trans_id;
this.filters = ['Identical', 'Different', 'Source Only', 'Target Only'];
this.sel_filters = ['Different', 'Source Only', 'Target Only'];
this.ignore_filters = ['owner', 'whitespaces'];
this.ignore_whitespaces = 0;
this.ignore_owner = 0;
this.dataView = null;
this.grid = null;
this.selection = {};
@ -179,6 +182,8 @@ export default class SchemaDiffUI {
this.selection = JSON.parse(JSON.stringify(url_params));
url_params['trans_id'] = self.trans_id;
url_params['ignore_owner'] = self.ignore_owner;
url_params['ignore_whitespaces'] = self.ignore_whitespaces;
_.each(url_params, function(key, val) {
url_params[key] = parseInt(val, 10);
@ -837,6 +842,13 @@ export default class SchemaDiffUI {
self.header.$el.find('button.btn-primary').on('click', self.compare_schemas.bind(self));
self.header.$el.find('button#generate-script').on('click', self.generate_script.bind(self));
self.header.$el.find('ul.filter a.dropdown-item').on('click', self.refresh_filters.bind(self));
self.header.$el.find('ul.ignore a.dropdown-item').on('click', self.refresh_ignore_settings.bind(self));
/* Set the default value for 'ignore owner' and 'ignore whitespace' */
let pref = pgWindow.pgAdmin.Browser.get_preferences_for_module('schema_diff');
if (pref.ignore_owner) self.header.$el.find('ul.ignore a.dropdown-item#btn-ignore-owner').click();
if (pref.ignore_whitespaces) self.header.$el.find('ul.ignore a.dropdown-item#btn-ignore-whitespaces').click();
let footer_panel = self.docker.findPanels('schema_diff_footer_panel')[0],
header_panel = self.docker.findPanels('schema_diff_header_panel')[0];
@ -876,6 +888,23 @@ export default class SchemaDiffUI {
}
}
refresh_ignore_settings(event) {
let self = this,
element = $(event.currentTarget).find('.fa-check');
if (element.length == 1) {
if (element.hasClass('visibility-hidden') === true) {
element.removeClass('visibility-hidden');
if (event.currentTarget.id === 'btn-ignore-owner') self.ignore_owner = 1;
if (event.currentTarget.id === 'btn-ignore-whitespaces') self.ignore_whitespaces = 1;
} else {
element.addClass('visibility-hidden');
if (event.currentTarget.id === 'btn-ignore-owner') self.ignore_owner = 0;
if (event.currentTarget.id === 'btn-ignore-whitespaces') self.ignore_whitespaces = 0;
}
}
}
connect_database(server_id, db_id, callback) {
var url = url_for('schema_diff.connect_database', {'sid': server_id, 'did': db_id});
$.post(url)