pgadmin4/web/pgadmin/tools/schema_diff/directory_compare.py

758 lines
28 KiB
Python
Raw Normal View History

##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2020, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Directory comparison"""
import copy
import string
from pgadmin.tools.schema_diff.model import SchemaDiffModel
count = 1
list_keys_array = ['name', 'colname', 'argid', 'token', 'option', 'conname',
'member_name', 'label', 'attname', 'fdwoption',
'fsrvoption', 'umoption']
def _get_source_list(added, source_dict, node, source_params, view_object,
node_label, group_name):
"""
Get only source list.
:param added: added dict list.
:param source_dict: source dict.
:param node: node type.
:param source_params: source parameters.
:param view_object: view object for get sql.
:param node_label: node label.
:param group_name: group name
:return: list of source dict.
"""
global count
source_only = []
for item in added:
source_object_id = None
if 'oid' in source_dict[item]:
source_object_id = source_dict[item]['oid']
if node == 'table':
temp_src_params = copy.deepcopy(source_params)
temp_src_params['tid'] = source_object_id
temp_src_params['json_resp'] = False
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
diff_ddl = view_object.get_sql_from_table_diff(**temp_src_params)
source_dependencies = \
view_object.get_table_submodules_dependencies(
**temp_src_params)
else:
temp_src_params = copy.deepcopy(source_params)
temp_src_params['oid'] = source_object_id
# Provide Foreign Data Wrapper ID
if 'fdwid' in source_dict[item]:
temp_src_params['fdwid'] = source_dict[item]['fdwid']
# Provide Foreign Server ID
if 'fsid' in source_dict[item]:
temp_src_params['fsid'] = source_dict[item]['fsid']
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
diff_ddl = view_object.get_sql_from_diff(**temp_src_params)
source_dependencies = view_object.get_dependencies(
view_object.conn, source_object_id, where=None,
show_system_objects=None, is_schema_diff=True)
source_only.append({
'id': count,
'type': node,
'label': node_label,
'title': item,
'oid': source_object_id,
'status': SchemaDiffModel.COMPARISON_STATUS['source_only'],
'source_ddl': source_ddl,
'target_ddl': '',
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': source_dependencies
})
count += 1
return source_only
def _delete_keys(temp_tgt_params):
"""
Delete keys from temp target parameters.
:param temp_tgt_params:
:type temp_tgt_params:
:return:
"""
if 'gid' in temp_tgt_params:
del temp_tgt_params['gid']
if 'json_resp' in temp_tgt_params:
del temp_tgt_params['json_resp']
def _get_target_list(removed, target_dict, node, target_params, view_object,
node_label, group_name):
"""
Get only target list.
:param removed: removed list.
:param target_dict: target dict.
:param node: node type.
:param target_params: target parameters.
:param view_object: view object for get sql.
:param node_label: node label.
:param group_name: group name.
:return: list of target dict.
"""
global count
target_only = []
for item in removed:
target_object_id = None
if 'oid' in target_dict[item]:
target_object_id = target_dict[item]['oid']
if node == 'table':
temp_tgt_params = copy.deepcopy(target_params)
temp_tgt_params['tid'] = target_object_id
temp_tgt_params['json_resp'] = False
target_ddl = view_object.get_sql_from_table_diff(**temp_tgt_params)
_delete_keys(temp_tgt_params)
diff_ddl = view_object.get_drop_sql(**temp_tgt_params)
else:
temp_tgt_params = copy.deepcopy(target_params)
temp_tgt_params['oid'] = target_object_id
# Provide Foreign Data Wrapper ID
if 'fdwid' in target_dict[item]:
temp_tgt_params['fdwid'] = target_dict[item]['fdwid']
# Provide Foreign Server ID
if 'fsid' in target_dict[item]:
temp_tgt_params['fsid'] = target_dict[item]['fsid']
target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
temp_tgt_params.update(
{'drop_sql': True})
diff_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
target_only.append({
'id': count,
'type': node,
'label': node_label,
'title': item,
'oid': target_object_id,
'status': SchemaDiffModel.COMPARISON_STATUS['target_only'],
'source_ddl': '',
'target_ddl': target_ddl,
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': []
})
count += 1
return target_only
def _check_add_req_ids(source_dict, target_dict, key, temp_src_params,
temp_tgt_params):
"""
Check for Foreign Data Wrapper ID and Foreign Server ID and update it
in req parameters.
:param source_dict: Source dict for compare schema.
:param target_dict: Target dict for compare schema.
:param key: Key for get obj.
:param temp_src_params:
:param temp_tgt_params:
:return:
"""
if 'fdwid' in source_dict[key]:
temp_src_params['fdwid'] = source_dict[key]['fdwid']
temp_tgt_params['fdwid'] = target_dict[key]['fdwid']
# Provide Foreign Server ID
if 'fsid' in source_dict[key]:
temp_src_params['fsid'] = source_dict[key]['fsid']
temp_tgt_params['fsid'] = target_dict[key]['fsid']
def get_source_target_oid(source_dict, target_dict, key):
"""
Get source and target object ID.
:param source_dict: Source schema diff data.
:param target_dict: Target schema diff data.
:param key: Key.
:return: source and target object ID.
"""
source_object_id = None
target_object_id = None
if 'oid' in source_dict[key]:
source_object_id = source_dict[key]['oid']
target_object_id = target_dict[key]['oid']
return source_object_id, target_object_id
def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
node, node_label, view_object,
**kwargs):
"""
get lists of identical and different keys list.
:param intersect_keys:
:param source_dict:
:param target_dict:
:param node:
:param node_label:
:param view_object:
:param other_param:
:return: return list of identical and different dict.
"""
global count
identical = []
different = []
dict1 = kwargs['dict1']
dict2 = kwargs['dict2']
ignore_whitespaces = kwargs['ignore_whitespaces']
ignore_keys = kwargs['ignore_keys']
source_params = kwargs['source_params']
target_params = kwargs['target_params']
group_name = kwargs['group_name']
for key in intersect_keys:
source_object_id, target_object_id = get_source_target_oid(source_dict,
target_dict,
key)
# Recursively Compare the two dictionary
if are_dictionaries_identical(dict1[key], dict2[key],
ignore_whitespaces, ignore_keys):
identical.append({
'id': count,
'type': node,
'label': node_label,
'title': key,
'oid': source_object_id,
'source_oid': source_object_id,
'target_oid': target_object_id,
'status': SchemaDiffModel.COMPARISON_STATUS['identical'],
'group_name': group_name,
'dependencies': [],
'source_scid': source_params['scid']
if 'scid' in source_params else 0,
'target_scid': target_params['scid']
if 'scid' in target_params else 0,
})
else:
if node == 'table':
temp_src_params = copy.deepcopy(source_params)
temp_tgt_params = copy.deepcopy(target_params)
# Add submodules into the ignore keys so that directory
# difference won't include those in added, deleted and changed
sub_module = ['index', 'rule', 'trigger', 'compound_trigger']
temp_ignore_keys = view_object.keys_to_ignore + sub_module
diff_dict = directory_diff(
dict1[key], dict2[key],
ignore_keys=temp_ignore_keys,
difference={}
)
parse_acl(dict1[key], dict2[key], diff_dict)
temp_src_params['tid'] = source_object_id
temp_tgt_params['tid'] = target_object_id
temp_src_params['json_resp'] = \
temp_tgt_params['json_resp'] = False
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
diff_dependencies = \
view_object.get_table_submodules_dependencies(
**temp_src_params)
target_ddl = \
view_object.get_sql_from_table_diff(**temp_tgt_params)
diff_ddl = view_object.get_sql_from_submodule_diff(
source_params=temp_src_params,
target_params=temp_tgt_params,
source=dict1[key], target=dict2[key], diff_dict=diff_dict,
ignore_whitespaces=ignore_whitespaces)
else:
temp_src_params = copy.deepcopy(source_params)
temp_tgt_params = copy.deepcopy(target_params)
diff_dict = directory_diff(
dict1[key], dict2[key],
ignore_keys=view_object.keys_to_ignore, difference={}
)
parse_acl(dict1[key], dict2[key], diff_dict)
temp_src_params['oid'] = source_object_id
temp_tgt_params['oid'] = target_object_id
# Provide Foreign Data Wrapper ID
_check_add_req_ids(source_dict, target_dict, key,
temp_src_params, temp_tgt_params)
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
diff_dependencies = view_object.get_dependencies(
view_object.conn, source_object_id, where=None,
show_system_objects=None, is_schema_diff=True)
target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
temp_tgt_params.update(
{'data': diff_dict})
diff_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
different.append({
'id': count,
'type': node,
'label': node_label,
'title': key,
'oid': source_object_id,
'source_oid': source_object_id,
'target_oid': target_object_id,
'status': SchemaDiffModel.COMPARISON_STATUS['different'],
'source_ddl': source_ddl,
'target_ddl': target_ddl,
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': diff_dependencies
})
count += 1
return identical, different
def compare_dictionaries(**kwargs):
"""
This function will compare the two dictionaries.
:param kwargs:
:return:
"""
view_object = kwargs.get('view_object')
source_params = kwargs.get('source_params')
target_params = kwargs.get('target_params')
group_name = kwargs.get('group_name')
source_dict = kwargs.get('source_dict')
target_dict = kwargs.get('target_dict')
node = kwargs.get('node')
node_label = kwargs.get('node_label')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
ignore_keys = kwargs.get('ignore_keys', None)
dict1 = copy.deepcopy(source_dict)
dict2 = copy.deepcopy(target_dict)
# Find the duplicate keys in both the dictionaries
dict1_keys = set(dict1.keys())
dict2_keys = set(dict2.keys())
intersect_keys = dict1_keys.intersection(dict2_keys)
# Add gid to the params
source_params['gid'] = target_params['gid'] = 1
# Keys that are available in source and missing in target.
added = dict1_keys - dict2_keys
source_only = _get_source_list(added, source_dict, node, source_params,
view_object, node_label, group_name)
target_only = []
# Keys that are available in target and missing in source.
removed = dict2_keys - dict1_keys
target_only = _get_target_list(removed, target_dict, node, target_params,
view_object, node_label, group_name)
# Compare the values of duplicates keys.
other_param = {
"dict1": dict1,
"dict2": dict2,
"ignore_whitespaces": ignore_whitespaces,
"ignore_keys": ignore_keys,
"source_params": source_params,
"target_params": target_params,
"group_name": group_name
}
identical, different = _get_identical_and_different_list(
intersect_keys, source_dict, target_dict, node, node_label,
view_object, **other_param)
return source_only + target_only + different + identical
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:
"""
if source_list is None or target_list is None or \
len(source_list) != len(target_list):
return False
else:
for index in range(len(source_list)):
# Check the type of the value if it is an dictionary then
# call are_dictionaries_identical() function.
if isinstance(source_list[index], dict):
if not are_dictionaries_identical(source_list[index],
target_list[index],
ignore_whitespaces,
ignore_keys):
return False
else:
if source_list[index] != target_list[index]:
return False
return True
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:
"""
src_keys = set(source_dict.keys())
tar_keys = set(target_dict.keys())
# Keys that are available in source and missing in target.
src_only = src_keys - tar_keys
# Keys that are available in target and missing in source.
tar_only = tar_keys - src_keys
# If number of keys are different in source and target then
# return False
if len(src_only) != len(tar_only):
return False
else:
# If number of keys are same but key is not present in target then
# return False
for key in src_only:
if key not in tar_only:
return False
for key in source_dict.keys():
# Continue if key is available in ignore_keys
if key in ignore_keys:
continue
if isinstance(source_dict[key], dict):
if not are_dictionaries_identical(source_dict[key],
target_dict[key],
ignore_whitespaces,
ignore_keys):
return False
elif isinstance(source_dict[key], list):
# Sort the source and target list on the basis of
# list key array.
source_dict[key], target_dict[key] = sort_list(source_dict[key],
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:
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
def directory_diff(source_dict, target_dict, ignore_keys=[], difference=None):
"""
This function is used to recursively compare two dictionaries and
return the difference.
The difference is from source to target
:param source_dict: source dict
:param target_dict: target dict
:param ignore_keys: ignore keys to compare
:param difference:
"""
difference = {} if difference is None else difference
src_keys = set(source_dict.keys())
tar_keys = set(target_dict.keys())
# Keys that are available in source and missing in target.
src_only = src_keys - tar_keys
# Keys that are available in target and missing in source.
tar_only = tar_keys - src_keys
for key in source_dict.keys():
added = []
deleted = []
updated = []
source = None
# ignore the keys if available.
if key in ignore_keys:
continue
elif key in tar_only:
if isinstance(target_dict[key], list):
difference[key] = {}
difference[key]['deleted'] = target_dict[key]
elif key in src_only:
# Source only values in the newly added list
if isinstance(source_dict[key], list):
difference[key] = {}
difference[key]['added'] = source_dict[key]
elif isinstance(source_dict[key], dict):
directory_diff(source_dict[key], target_dict[key],
ignore_keys, difference)
elif isinstance(source_dict[key], list):
tmp_target = None
tmp_list = [x for x in source_dict[key]
if isinstance(x, (list, dict))]
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
if tmp_list:
tmp_target = copy.deepcopy(target_dict[key])
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
for index in range(len(source_dict[key])):
source = copy.deepcopy(source_dict[key][index])
if isinstance(source, list):
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
# TODO
pass
elif isinstance(source, dict):
# Check the above keys are exist in the dictionary
tmp_key = is_key_exists(list_keys_array, source)
if tmp_key is not None:
# Compare the two list by ignoring the keys.
compare_list_by_ignoring_keys(source, tmp_target,
added, updated,
tmp_key, ignore_keys)
difference[key] = {}
if len(added) > 0:
difference[key]['added'] = added
if len(updated) > 0:
difference[key]['changed'] = updated
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
elif target_dict[key] is None or \
(isinstance(target_dict[key], list) and
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
len(target_dict[key]) < index and
source != target_dict[key][index]):
difference[key] = source
elif isinstance(target_dict[key], list) and\
Fixed following schema diff issues: 1) Version mismatch should be displayed if you select EPAS 11 as the source and EPAS 12 as the target. 2) We should handle schema diff if the user stops the server after compare. 3) The data type is not visible for column headers in the query tool/view data. 4) Difference SQL is shown, though source & target SQL are same. 5) Error is shown when the 'target only' table is selected & clicked on Generate Script. 6) Difference SQL generated on deleting primary constraints from source throwing error on running from query tool. 7) Copy button for Difference SQL does not work. 8) Incorrect SQL is generated when check constraint from the source table is dropped. 9) Difference SQL is NOT shown when 'Unique Constraint' is dropped from the source table. 10) In case of difference, no message displayed related to copied successfully or not. 11) create or replace trigger should be on the next line. 12) Comparison Result of exactly identical tables having foreign key constraints is shown as different. 13) The wrong SQL displayed in the difference tab for view and materialized view for the select statements. 14) Wrong SQL displayed for the procedure in the difference section. 15) If the user already opens the compare section of Schema diff tool and Disconnects the server then '<' not supported between instances of 'NoneType' and 'int' message displayed. 16) When 'check constraint' is added on the source table which has already unique constraint & identical target table is created, all SQL panels remain empty. 17) Difference SQL is NOT shown when after adding 'Foreign Key' constraint to existing source table. 18) Incorrect SQL is generated when the existing index on the source table is modified. 19) Wrong SQL displayed for function in difference section. 20) Additional space is added before & after in difference SQL generated on the addition of an index to the source table. 21) Difference SQL is NOT shown when tables have different permission/grants. 22) Incorrect SQL is shown when the source had inherited table & target has a normal table. 23) Exactly identical child(inherited) tables show difference SQL. 24) Comparison is NOT working when the custom vacuum is enabled & one of the parameter modified & again custom vacuum is disabled.
2020-01-28 03:23:17 -06:00
len(target_dict[key]) > index:
difference[key] = source
elif len(source_dict[key]) > 0:
difference[key] = source_dict[key]
elif key in target_dict and isinstance(target_dict[key], list):
# If no element in source dict then check for the element
# is available in target and the type is of list.
# Added such elements as a deleted.
tmp_tar_list = [x for x in target_dict[key]
if isinstance(x, (list, dict))]
if tmp_tar_list:
difference[key] = {'deleted': target_dict[key]}
if isinstance(source, dict) and tmp_target and key in tmp_target \
and tmp_target[key] and len(tmp_target[key]) > 0:
if isinstance(tmp_target[key], list) and \
isinstance(tmp_target[key][0], dict):
deleted = deleted + tmp_target[key]
else:
deleted.append({key: tmp_target[key]})
difference[key]['deleted'] = deleted
elif tmp_target and isinstance(tmp_target, list):
difference[key]['deleted'] = tmp_target
# No point adding empty list into difference.
if key in difference and len(difference[key]) == 0:
difference.pop(key)
else:
if source_dict[key] != target_dict[key]:
if (key == 'comment' or key == 'description') and \
source_dict[key] is None:
difference[key] = ''
else:
difference[key] = source_dict[key]
return difference
def is_key_exists(key_list, target_dict):
"""
This function is used to iterate the key list and check that key is
present in the given dictionary
:param key_list:
:param target_dict:
:return:
"""
for key in key_list:
if key in target_dict:
return key
return None
def _check_key_in_source_target(key, acl_keys, target, source):
"""
Check if key is present in source if not then check it's present in target.
:param key: key to be checked.
:param acl_keys: acl keys
:param target: target object.
:param source: source object.
:return: return key.
"""
if key is None:
key = is_key_exists(acl_keys, target)
if key is None:
key = 'acl'
elif key is not None and not isinstance(source[key], list):
key = 'acl'
return key
def parse_acl(source, target, diff_dict):
"""
This function is used to parse acl.
:param source: Source Dict
:param target: Target Dict
:param diff_dict: Difference Dict
"""
acl_keys = ['datacl', 'relacl', 'typacl', 'pkgacl']
key = is_key_exists(acl_keys, source)
# If key is not found in source then check the key is available
# in target.
key = _check_key_in_source_target(key, acl_keys, target, source)
tmp_source = source[key] if\
key in source and source[key] is not None else []
tmp_target = copy.deepcopy(target[key]) if\
key in target and target[key] is not None else []
diff = {'added': [], 'deleted': []}
for acl in tmp_source:
if acl in tmp_target:
tmp_target.remove(acl)
elif acl not in tmp_target:
diff['added'].append(acl)
diff['deleted'] = tmp_target
# Update the key if there are some element in added or deleted
# else remove that key from diff dict
if len(diff['added']) > 0 or len(diff['deleted']) > 0:
diff_dict.update({key: diff})
elif key in diff_dict:
diff_dict.pop(key)
def sort_list(source, target):
"""
This function is used to sort the source and target list on the
basis of key found in the source and target list.
:param source:
:param target:
:return:
"""
# Check the above keys are exist in the dictionary
if source is not None and source and isinstance(source[0], dict):
tmp_key = is_key_exists(list_keys_array, source[0])
if tmp_key is not None:
source = sorted(source, key=lambda k: k[tmp_key])
# Check the above keys are exist in the dictionary
if target is not None and target and isinstance(target[0], dict):
tmp_key = is_key_exists(list_keys_array, target[0])
if tmp_key is not None:
target = sorted(target, key=lambda k: k[tmp_key])
return source, target
def compare_list_by_ignoring_keys(source_list, target_list, added, updated,
key, ignore_keys):
"""
This function is used to compare the two list by ignoring the keys
specified in ignore_keys.
:param source_list:
:param target_list:
:param added:
:param updated:
:param key:
:param ignore_keys:
:return:
"""
if isinstance(target_list, list) and target_list:
tmp_target = None
for item in target_list:
if key in item and item[key] == source_list[key]:
tmp_target = copy.deepcopy(item)
if tmp_target is None:
added.append(source_list)
else:
source_with_ignored_keys = copy.deepcopy(source_list)
target_with_ignored_keys = copy.deepcopy(tmp_target)
# Remove ignore keys from source and target before comparison
_remove_keys(ignore_keys, source_with_ignored_keys,
target_with_ignored_keys)
_compare_source_and_target(source_with_ignored_keys,
target_with_ignored_keys, source_list,
target_list, updated, tmp_target)
else:
added.append(source_list)
def _remove_keys(ignore_keys, source_with_ignored_keys,
target_with_ignored_keys):
"""
Remove non required keys form both source and target object.
:param ignore_keys: ignore keys list.
:param source_with_ignored_keys: source keys list.
:param target_with_ignored_keys: target keys list.
:return: None
"""
for ig_key in ignore_keys:
if ig_key in source_with_ignored_keys:
del source_with_ignored_keys[ig_key]
if ig_key in target_with_ignored_keys:
del target_with_ignored_keys[ig_key]
def _compare_source_and_target(source_with_ignored_keys,
target_with_ignored_keys, source_list,
target_list, updated, tmp_target):
"""
Compare source and target keys
:param source_with_ignored_keys:
:param target_with_ignored_keys:
:param source_list:
:param target_list:
:param updated:
:param tmp_target:
:return:
"""
if source_with_ignored_keys != target_with_ignored_keys:
updated.append(source_list)
target_list.remove(tmp_target)
elif source_with_ignored_keys == target_with_ignored_keys:
target_list.remove(tmp_target)