1) Added option to ignore the whitespaces while comparing objects in schema diff. Fixes #5468
2) Added server group name while selecting servers in schema diff. Fixes #5500 3) Fixed an issue where two identical tables showing different by schema diff tool. Fixes #5584pull/33/head
parent
9f5e8962b5
commit
cb268075c2
Binary file not shown.
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 117 KiB |
|
@ -390,6 +390,9 @@ Expand the *Schema Diff* node to specify your display preferences.
|
|||
:alt: Preferences schema diff
|
||||
:align: center
|
||||
|
||||
Use the *Ignore whitespaces* switch to ignores the whitespaces while comparing
|
||||
the string objects. Whitespace includes space, tabs, and CRLF.
|
||||
|
||||
Use the *Open in new browser tab* switch to indicate if you would like Schema Diff
|
||||
to open in a new tab.
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ This release contains a number of bug fixes and new features since the release o
|
|||
New features
|
||||
************
|
||||
|
||||
| `Issue #5468 <https://redmine.postgresql.org/issues/5468>`_ - Added option to ignore the whitespaces while comparing objects in schema diff.
|
||||
| `Issue #5500 <https://redmine.postgresql.org/issues/5500>`_ - Added server group name while selecting servers in schema diff.
|
||||
| `Issue #5516 <https://redmine.postgresql.org/issues/5516>`_ - Added support of Row Security Policies.
|
||||
| `Issue #5576 <https://redmine.postgresql.org/issues/5576>`_ - Improve error messaging if the storage and log directories cannot be created.
|
||||
|
||||
|
@ -29,4 +31,5 @@ Bug fixes
|
|||
| `Issue #5507 <https://redmine.postgresql.org/issues/5507>`_ - Fixed connection and version number detection issue when the database server is upgraded.
|
||||
| `Issue #5521 <https://redmine.postgresql.org/issues/5521>`_ - Fixed an issue when dumping servers from a desktop pgAdmin app by providing an option '--sqlite-path'.
|
||||
| `Issue #5539 <https://redmine.postgresql.org/issues/5539>`_ - Fixed typo in exception keyword.
|
||||
| `Issue #5584 <https://redmine.postgresql.org/issues/5584>`_ - Fixed an issue where two identical tables showing different by schema diff tool.
|
||||
| `Issue #5592 <https://redmine.postgresql.org/issues/5592>`_ - Ensure that pgadmin should be able to connect to the server which has password more than 1000 characters.
|
|
@ -158,7 +158,7 @@ def column_formatter(conn, tid, clid, data, edit_types_list=None,
|
|||
|
||||
# We will need present type in edit mode
|
||||
edit_types_list.append(data['cltype'])
|
||||
data['edit_types'] = edit_types_list
|
||||
data['edit_types'] = sorted(edit_types_list)
|
||||
|
||||
data['cltype'] = DataTypeReader.parse_type_name(data['cltype'])
|
||||
|
||||
|
@ -205,7 +205,7 @@ def get_formatted_columns(conn, tid, data, other_columns,
|
|||
edit_types.keys())))
|
||||
status, res = conn.execute_2darray(SQL)
|
||||
for row in res['rows']:
|
||||
edit_types[row['main_oid']] = row['edit_types']
|
||||
edit_types[row['main_oid']] = sorted(row['edit_types'])
|
||||
|
||||
for column in data['columns']:
|
||||
column_formatter(conn, tid, column['attnum'], column,
|
||||
|
|
|
@ -227,7 +227,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
|
|||
})
|
||||
|
||||
# Schema Diff: Keys to ignore while comparing
|
||||
keys_to_ignore = ['oid', 'relowner', 'schema',
|
||||
keys_to_ignore = ['oid', 'relowner', 'schema', 'indclass',
|
||||
'indrelid', 'nspname', 'oid-2']
|
||||
|
||||
def check_precondition(f):
|
||||
|
@ -1068,11 +1068,11 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
|
|||
create_req = True
|
||||
|
||||
if create_req:
|
||||
diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],
|
||||
did=tgt_params['did'],
|
||||
scid=tgt_params['scid'],
|
||||
tid=tgt_params['tid'],
|
||||
idx=target['oid'],
|
||||
diff = self.get_sql_from_index_diff(sid=src_params['sid'],
|
||||
did=src_params['did'],
|
||||
scid=src_params['scid'],
|
||||
tid=src_params['tid'],
|
||||
idx=source['oid'],
|
||||
diff_schema=target_schema,
|
||||
drop_req=True)
|
||||
else:
|
||||
|
|
|
@ -22,7 +22,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
table_keys_to_ignore = ['oid', 'schema', 'edit_types', 'attnum',
|
||||
'col_type', 'references', 'reltuples', 'oid-2',
|
||||
'rows_cnt', 'seqrelid', 'atttypid', 'elemoid',
|
||||
'hastoasttable', 'relhassubclass']
|
||||
'hastoasttable', 'relhassubclass', 'relacl_str']
|
||||
|
||||
constraint_keys_to_ignore = ['relname', 'nspname', 'parent_tbl',
|
||||
'attrelid', 'adrelid', 'fknsp', 'confrelid',
|
||||
|
@ -31,7 +31,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
|
||||
trigger_keys_to_ignore = ['xmin', 'tgrelid', 'tgfoid', 'tfunction',
|
||||
'tgqual', 'tgconstraint']
|
||||
index_keys_to_ignore = ['relowner', 'indrelid']
|
||||
index_keys_to_ignore = ['relowner', 'indrelid', 'indclass']
|
||||
|
||||
keys_to_ignore = table_keys_to_ignore + constraint_keys_to_ignore \
|
||||
+ trigger_keys_to_ignore + index_keys_to_ignore
|
||||
|
@ -51,6 +51,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
'did': kwargs.get('target_did'),
|
||||
'scid': kwargs.get('target_scid')}
|
||||
|
||||
ignore_whitespaces = kwargs.get('ignore_whitespaces')
|
||||
status, target_schema = self.get_schema(**target_params)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=target_schema)
|
||||
|
@ -68,6 +69,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
target_tables,
|
||||
self.node_type,
|
||||
self.blueprint.COLLECTION_LABEL,
|
||||
ignore_whitespaces,
|
||||
self.keys_to_ignore)
|
||||
|
||||
def ddl_compare(self, **kwargs):
|
||||
|
@ -225,7 +227,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
return different
|
||||
|
||||
def get_sql_from_submodule_diff(self, source_params, target_params,
|
||||
target_schema, source, target, diff_dict):
|
||||
target_schema, source, target, diff_dict,
|
||||
ignore_whitespaces):
|
||||
"""
|
||||
This function returns the DDL/DML statements of the
|
||||
submodules of table based on the comparison status.
|
||||
|
@ -236,6 +239,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
:param source:
|
||||
:param target:
|
||||
:param diff_dict:
|
||||
:param ignore_whitespaces:
|
||||
:return:
|
||||
"""
|
||||
# Get the difference result for source and target columns
|
||||
|
@ -250,7 +254,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
target_params['diff_data'] = diff_dict
|
||||
diff = self.get_sql_from_table_diff(**target_params)
|
||||
|
||||
ignore_sub_modules = ['column', 'constraints']
|
||||
ignore_sub_modules = ['column', 'constraints', 'row_security_policy']
|
||||
if self.manager.version < 100000:
|
||||
ignore_sub_modules.append('partition')
|
||||
if self.manager.server_type == 'pg' or self.manager.version < 120000:
|
||||
|
@ -314,7 +318,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||
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], ignore_whitespaces,
|
||||
self.keys_to_ignore):
|
||||
|
||||
diff_ddl = module_view.ddl_compare(
|
||||
source_params=source_params,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
@import '~codemirror/addon/scroll/simplescrollbars.css';
|
||||
|
||||
@import '~slickgrid/slick.grid.css';
|
||||
@import '~slickgrid/slick-default-theme.css';
|
||||
@import '~slickgrid/css/smoothness/jquery-ui-1.11.3.custom.css';
|
||||
|
||||
@import '../vendor/backgrid/backgrid.css';
|
||||
|
|
|
@ -24,6 +24,7 @@ from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
|||
from pgadmin.tools.schema_diff.model import SchemaDiffModel
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
|
||||
MODULE_NAME = 'schema_diff'
|
||||
|
||||
|
@ -79,6 +80,16 @@ class SchemaDiffModule(PgAdminModule):
|
|||
'will be opened in a new browser tab.')
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'display', 'ignore_whitespaces',
|
||||
gettext("Ignore whitespaces"), 'boolean', False,
|
||||
category_label=gettext('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')
|
||||
)
|
||||
|
||||
|
||||
blueprint = SchemaDiffModule(MODULE_NAME, __name__, static_url_path='/static')
|
||||
|
||||
|
@ -257,7 +268,7 @@ def servers():
|
|||
This function will return the list of servers for the specified
|
||||
server id.
|
||||
"""
|
||||
res = []
|
||||
res = {}
|
||||
try:
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
|
@ -269,15 +280,19 @@ def servers():
|
|||
manager = driver.connection_manager(server.id)
|
||||
conn = manager.connection()
|
||||
connected = conn.connected()
|
||||
|
||||
res.append({
|
||||
server_info = {
|
||||
"value": server.id,
|
||||
"label": server.name,
|
||||
"image": server_icon_and_background(connected, manager,
|
||||
server),
|
||||
"_id": server.id,
|
||||
"connected": connected,
|
||||
})
|
||||
"connected": connected
|
||||
}
|
||||
|
||||
if server.servers.name in res:
|
||||
res[server.servers.name].append(server_info)
|
||||
else:
|
||||
res[server.servers.name] = [server_info]
|
||||
|
||||
except Exception as e:
|
||||
app.logger.exception(e)
|
||||
|
@ -443,6 +458,9 @@ def compare(trans_id, source_sid, source_did, source_scid,
|
|||
diff_model_obj)
|
||||
|
||||
try:
|
||||
pref = Preferences.module('schema_diff')
|
||||
ignore_whitespaces = pref.preference('ignore_whitespaces').get()
|
||||
|
||||
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes()
|
||||
node_percent = round(100 / len(all_registered_nodes))
|
||||
total_percent = 0
|
||||
|
@ -462,7 +480,8 @@ def compare(trans_id, source_sid, source_did, source_scid,
|
|||
source_scid=source_scid,
|
||||
target_sid=target_sid,
|
||||
target_did=target_did,
|
||||
target_scid=target_scid)
|
||||
target_scid=target_scid,
|
||||
ignore_whitespaces=ignore_whitespaces)
|
||||
|
||||
if res is not None:
|
||||
comparison_result = comparison_result + res
|
||||
|
|
|
@ -68,6 +68,7 @@ class SchemaDiffObjectCompare:
|
|||
'scid': kwargs.get('target_scid')
|
||||
}
|
||||
|
||||
ignore_whitespaces = kwargs.get('ignore_whitespaces')
|
||||
status, target_schema = self.get_schema(kwargs.get('target_sid'),
|
||||
kwargs.get('target_did'),
|
||||
kwargs.get('target_scid')
|
||||
|
@ -88,6 +89,7 @@ class SchemaDiffObjectCompare:
|
|||
target_schema, source, target,
|
||||
self.node_type,
|
||||
gettext(self.blueprint.COLLECTION_LABEL),
|
||||
ignore_whitespaces,
|
||||
self.keys_to_ignore)
|
||||
|
||||
def ddl_compare(self, **kwargs):
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"""Directory comparison"""
|
||||
|
||||
import copy
|
||||
import string
|
||||
from pgadmin.tools.schema_diff.model import SchemaDiffModel
|
||||
|
||||
count = 1
|
||||
|
@ -20,7 +21,7 @@ list_keys_array = ['name', 'colname', 'argid', 'token', 'option', 'conname',
|
|||
|
||||
def compare_dictionaries(view_object, source_params, target_params,
|
||||
target_schema, source_dict, target_dict, node,
|
||||
node_label,
|
||||
node_label, ignore_whitespaces,
|
||||
ignore_keys=None):
|
||||
"""
|
||||
This function will compare the two dictionaries.
|
||||
|
@ -33,6 +34,7 @@ def compare_dictionaries(view_object, source_params, target_params,
|
|||
:param target_dict: Second Dictionary
|
||||
:param node: node type
|
||||
:param node_label: node label
|
||||
:param ignore_whitespaces: If set the True then ignore whitespaces
|
||||
:param ignore_keys: List of keys that will be ignored while comparing
|
||||
:return:
|
||||
"""
|
||||
|
@ -139,7 +141,8 @@ def compare_dictionaries(view_object, source_params, target_params,
|
|||
target_object_id = target_dict[key]['oid']
|
||||
|
||||
# Recursively Compare the two dictionary
|
||||
if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys):
|
||||
if are_dictionaries_identical(dict1[key], dict2[key],
|
||||
ignore_whitespaces, ignore_keys):
|
||||
identical.append({
|
||||
'id': count,
|
||||
'type': node,
|
||||
|
@ -177,7 +180,7 @@ def compare_dictionaries(view_object, source_params, target_params,
|
|||
view_object.get_sql_from_table_diff(**temp_tgt_params)
|
||||
diff_ddl = view_object.get_sql_from_submodule_diff(
|
||||
temp_src_params, temp_tgt_params, target_schema,
|
||||
dict1[key], dict2[key], diff_dict)
|
||||
dict1[key], dict2[key], diff_dict, ignore_whitespaces)
|
||||
else:
|
||||
temp_src_params = copy.deepcopy(source_params)
|
||||
temp_tgt_params = copy.deepcopy(target_params)
|
||||
|
@ -213,11 +216,13 @@ def compare_dictionaries(view_object, source_params, target_params,
|
|||
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_whitespaces,
|
||||
ignore_keys):
|
||||
"""
|
||||
This function is used to compare two list.
|
||||
:param source_list:
|
||||
:param target_list:
|
||||
:param ignore_whitespaces: ignore whitespaces
|
||||
:param ignore_keys: ignore keys to compare
|
||||
:return:
|
||||
"""
|
||||
|
@ -231,6 +236,7 @@ def are_lists_identical(source_list, target_list, ignore_keys):
|
|||
if type(source_list[index]) is dict:
|
||||
if not are_dictionaries_identical(source_list[index],
|
||||
target_list[index],
|
||||
ignore_whitespaces,
|
||||
ignore_keys):
|
||||
return False
|
||||
else:
|
||||
|
@ -239,12 +245,14 @@ 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_whitespaces,
|
||||
ignore_keys):
|
||||
"""
|
||||
This function is used to recursively compare two dictionaries with
|
||||
same keys.
|
||||
:param source_dict: source dict
|
||||
:param target_dict: target dict
|
||||
:param ignore_whitespaces: If set to True then ignore whitespaces
|
||||
:param ignore_keys: ignore keys to compare
|
||||
:return:
|
||||
"""
|
||||
|
@ -275,7 +283,9 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
|
|||
|
||||
if type(source_dict[key]) is dict:
|
||||
if not are_dictionaries_identical(source_dict[key],
|
||||
target_dict[key], ignore_keys):
|
||||
target_dict[key],
|
||||
ignore_whitespaces,
|
||||
ignore_keys):
|
||||
return False
|
||||
elif type(source_dict[key]) is list:
|
||||
# Sort the source and target list on the basis of
|
||||
|
@ -284,10 +294,25 @@ 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_whitespaces,
|
||||
ignore_keys):
|
||||
return False
|
||||
else:
|
||||
if source_dict[key] != target_dict[key]:
|
||||
source_value = source_dict[key]
|
||||
target_value = target_dict[key]
|
||||
|
||||
# If ignore_whitespaces is True then check the source_value and
|
||||
# target_value if of type string. If the values is of type string
|
||||
# then using translate function ignore all the whitespaces.
|
||||
if ignore_whitespaces:
|
||||
if isinstance(source_value, str):
|
||||
source_value = source_value.translate(
|
||||
str.maketrans('', '', string.whitespace))
|
||||
if isinstance(target_value, str):
|
||||
target_value = target_value.translate(
|
||||
str.maketrans('', '', string.whitespace))
|
||||
|
||||
if source_value != target_value:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -124,16 +124,36 @@ let SchemaDiffSelect2Control =
|
|||
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
|
||||
' <%=select2.first_empty ? " <option></option>" : ""%>',
|
||||
' <% for (var i=0; i < options.length; i++) {%>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <% if (options[i].group) { %>',
|
||||
' <% var group = options[i].group; %>',
|
||||
' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
|
||||
' <optgroup label="<%=group%>">',
|
||||
' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
|
||||
' <% var option = options[i].optval[subindex]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' </optgroup>',
|
||||
' <%}%>',
|
||||
' <%} else {%>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' <%}%>',
|
||||
' </select>',
|
||||
' <% if (helpMessage && helpMessage.length) { %>',
|
||||
|
|
|
@ -556,6 +556,15 @@ export default class SchemaDiffUI {
|
|||
fields: [{
|
||||
name: 'source_sid', label: false,
|
||||
control: SchemaDiffSelect2Control,
|
||||
transform: function(data) {
|
||||
let group_template_options = [];
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
group_template_options.push({'group': key, 'optval': data[key]});
|
||||
}
|
||||
}
|
||||
return group_template_options;
|
||||
},
|
||||
url: url_for('schema_diff.servers'),
|
||||
select2: {
|
||||
allowClear: true,
|
||||
|
@ -636,6 +645,15 @@ export default class SchemaDiffUI {
|
|||
}, {
|
||||
name: 'target_sid', label: false,
|
||||
control: SchemaDiffSelect2Control,
|
||||
transform: function(data) {
|
||||
let group_template_options = [];
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
group_template_options.push({'group': key, 'optval': data[key]});
|
||||
}
|
||||
}
|
||||
return group_template_options;
|
||||
},
|
||||
group: 'target',
|
||||
url: url_for('schema_diff.servers'),
|
||||
select2: {
|
||||
|
|
Loading…
Reference in New Issue