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.
This commit is contained in:
Khushboo Vashi 2020-01-28 14:53:17 +05:30 committed by Akshay Joshi
parent f167d77b61
commit c418a9c9ca
28 changed files with 199 additions and 127 deletions

View File

@ -38,7 +38,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -40,7 +40,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -35,7 +35,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -36,7 +36,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -38,7 +38,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -34,7 +34,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -40,7 +40,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -35,7 +35,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -36,7 +36,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -38,7 +38,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -34,7 +34,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -28,7 +28,7 @@ CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({% if
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -37,7 +37,7 @@ CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({% if
{% endif %}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}

View File

@ -837,5 +837,4 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
return sql
SchemaDiffRegistry(blueprint.node_type, PackageView)
PackageView.register_node_view(blueprint)

View File

@ -1607,10 +1607,21 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
data={'total_rows': count}
)
def get_delete_sql(self, res):
self.cmd = 'delete'
sql = super(TableView, self).get_delete_sql(res)
self.cmd = None
@BaseTableView.check_precondition
def get_drop_sql(self, sid, did, scid, tid):
SQL = render_template("/".join(
[self.table_template_path, 'properties.sql']),
did=did, scid=scid, tid=tid,
datlastsysoid=self.datlastsysoid
)
status, res = self.conn.execute_dict(SQL)
sql = ''
if status:
self.cmd = 'delete'
sql = super(TableView, self).get_delete_sql(res)
self.cmd = None
return sql
@BaseTableView.check_precondition

View File

@ -101,6 +101,7 @@ def get_check_constraint_sql(conn, tid, data, template_path=None):
if 'deleted' in constraint:
for c in constraint['deleted']:
c['schema'] = data['schema']
c['nspname'] = data['schema']
c['table'] = data['name']
# Sql for drop

View File

@ -137,14 +137,22 @@ def get_index_constraint_sql(conn, did, tid, data, template_path=None):
# If constraint(s) is/are deleted
if 'deleted' in constraint:
for c in constraint['deleted']:
c['schema'] = data['schema']
c['table'] = data['name']
del_cols = []
if 'columns_to_be_dropped' in data:
del_cols = list(map(lambda x, y: x['column'] in y,
c['columns'],
data['columns_to_be_dropped'])
)
# Sql for drop
sql.append(render_template("/".join([template_path,
'delete.sql']),
data=c,
conn=conn).strip('\n'))
if len(del_cols) == 0:
c['schema'] = data['schema']
c['table'] = data['name']
# Sql for drop
sql.append(render_template("/".join([template_path,
'delete.sql']),
data=c,
conn=conn).strip('\n'))
if 'changed' in constraint:
for c in constraint['changed']:
c['schema'] = data['schema']

View File

@ -834,23 +834,35 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
schema = ''
if data:
schema = self.schema
data['schema'] = self.schema
data['nspname'] = self.schema
data['table'] = self.table
sql, name = index_utils.get_sql(
self.conn, data, did, tid, idx, self.datlastsysoid,
mode='create')
sql = sql.strip('\n').strip(' ')
elif diff_schema:
schema = diff_schema
sql = index_utils.get_reverse_engineered_sql(
self.conn, schema,
self.table, did, tid, idx,
self.datlastsysoid,
template_path=None, with_header=False)
sql = index_utils.get_reverse_engineered_sql(
self.conn, schema,
self.table, did, tid, idx,
self.datlastsysoid,
template_path=None, with_header=False)
drop_sql = ''
if drop_req:
drop_sql = '\n' + render_template(
"/".join([self.template_path, 'delete.sql']),
data=data, conn=self.conn
)
drop_sql = '\n' + self.delete(gid=1, sid=sid, did=did,
scid=scid, tid=tid,
idx=idx, only_sql=True)
return drop_sql + '\n\n' + sql
if drop_sql != '':
sql = drop_sql + '\n\n' + sql
return sql
@check_precondition
def dependents(self, gid, sid, did, scid, tid, idx):
@ -1077,14 +1089,24 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
for key in required_create_keys:
if key in diff_dict:
create_req = True
if key == 'columns' and ((
'added' in diff_dict[key] and
len(diff_dict[key]['added']) > 0
) or ('changed' in diff_dict[key] and
len(diff_dict[key]['changed']) > 0) or (
'deleted' in diff_dict[key] and
len(diff_dict[key]['deleted']) > 0)
):
create_req = True
elif key != 'columns':
create_req = True
if create_req:
diff = self.get_sql_from_index_diff(sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_tid,
idx=src_oid,
diff = self.get_sql_from_index_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_tid,
idx=tar_oid,
diff_schema=target_schema,
drop_req=True)
else:

View File

@ -32,6 +32,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
'columns',
'edit_types',
'primary_key',
'unique_constraint',
'exclude_constraint',
'check_constraint',
'foreign_key',
@ -188,15 +189,9 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
scid=tar_scid,
tid=tar_oid,
json_resp=False)
SQL = render_template(
"/".join([self.table_template_path, 'properties.sql']),
did=tar_did, scid=tar_scid, tid=tar_oid,
datlastsysoid=self.datlastsysoid
)
status, res = self.conn.execute_dict(SQL)
if status:
diff = self.get_delete_sql(res)
diff = self.get_drop_sql(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['different']:
source = self.fetch_tables(
@ -230,10 +225,10 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
diff_dict.update(col_diff)
# Constraint comparison
pk_diff = self.constraint_ddl_comp(source, target)
pk_diff = self.constraint_ddl_comp(source, target, diff_dict)
diff_dict.update(pk_diff)
diff_dict['relacl'] = self.parce_acl(source, target)
diff_dict.update(self.parce_acl(source, target))
if not generate_script:
source = self.get_sql_from_table_diff(sid=src_sid,
@ -308,7 +303,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
)
if child_diff:
diff += child_diff
diff += '\n' + child_diff
elif result:
# For partition module
identical = False
@ -348,7 +343,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
comp_status=res['status']
)
if ddl_compare:
if child_diff:
diff += child_diff
else:
diff = self.get_sql_from_table_diff(
@ -418,7 +413,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
return different
@staticmethod
def constraint_ddl_comp(source_table, target_table):
def constraint_ddl_comp(source_table, target_table, diff_dict):
"""
Table Constraint DDL comparison
:param source: Source Table
@ -427,19 +422,26 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
"""
different = {}
non_editable_keys = {}
columns_to_be_dropped = []
non_editable_keys = {'primary_key': ['col_count',
'condeferrable',
'condeffered',
'columns'],
'unique_constraint': ['col_count',
'condeferrable',
'condeffered',
'columns'],
'check_constraint': ['consrc'],
'exclude_constraint': ['amname',
'indconstraint',
'columns']
'columns'],
'foreign_key': []
}
for constraint in ['primary_key', 'check_constraint',
'exclude_constraint']:
for constraint in ['primary_key', 'unique_constraint',
'check_constraint',
'exclude_constraint', 'foreign_key']:
source_cols = source_table[constraint] if \
constraint in source_table else []
target_cols = copy.deepcopy(target_table[constraint]) if\
@ -472,7 +474,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
tmp_updated = None
break
if tmp_updated:
tmp_updated['oid'] = tmp_tar['oid']
tmp_updated['oid'] = tmp['oid']
updated.append(tmp_updated)
target_cols.remove(tmp)
elif tmp_tar and tmp_src == tmp_tar:

View File

@ -91,7 +91,7 @@ PLAIN{% elif data.attstorage == 'm'%}MAIN{% elif data.attstorage == 'e'%}
EXTERNAL{% elif data.attstorage == 'x'%}EXTENDED{% endif %};
{% endif %}
{% if data.description is defined %}
{% if data.description is defined and data.description != None %}
{% if data.name %}
COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, data.name)}}
{% else %}

View File

@ -9,7 +9,7 @@ ALTER INDEX {{conn|qtIdent(data.schema, o_data.name)}}
ALTER INDEX {{conn|qtIdent(data.schema, data.name)}}
SET (FILLFACTOR={{data.fillfactor}});
{% elif data.fillfactor == '' and o_data.fillfactor|default('', 'true') != data.fillfactor %}
{% elif (data.fillfactor == '' or data.fillfactor == None) and o_data.fillfactor|default('', 'true') != data.fillfactor %}
ALTER INDEX {{conn|qtIdent(data.schema, data.name)}}
RESET (FILLFACTOR);

View File

@ -883,7 +883,8 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
old_col_data['cltype'])
# Sql for alter column
if 'inheritedfrom' not in c:
if 'inheritedfrom' not in c and\
'inheritedfromtable' not in c:
column_sql += render_template("/".join(
[self.column_template_path, 'update.sql']),
data=c, o_data=old_col_data, conn=self.conn
@ -897,7 +898,8 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
c = column_utils.convert_length_precision_to_string(c)
if 'inheritedfrom' not in c:
if 'inheritedfrom' not in c and\
'inheritedfromtable' not in c:
column_sql += render_template("/".join(
[self.column_template_path, 'create.sql']),
data=c, conn=self.conn).strip('\n') + '\n\n'
@ -950,6 +952,11 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
# Combine all the SQL together
SQL += '\n' + partitions_sql.strip('\n')
data['columns_to_be_dropped'] = []
if 'columns' in data and 'deleted' in data['columns']:
data['columns_to_be_dropped'] = list(map(
lambda d: d['name'], data['columns']['deleted']))
# Check if index constraints are added/changed/deleted
index_constraint_sql = \
idxcons_utils.get_index_constraint_sql(
@ -1501,11 +1508,12 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
for data_row in data[vacuum_key]['changed']:
for old_data_row in old_data[vacuum_key]:
if data_row['name'] == old_data_row['name']:
if data_row['value'] is not None:
set_values.append(data_row)
elif data_row['value'] is None and \
'value' in old_data_row:
reset_values.append(data_row)
if 'value' in data_row:
if data_row['value'] is not None:
set_values.append(data_row)
elif data_row['value'] is None and \
'value' in old_data_row:
reset_values.append(data_row)
if len(set_values) > 0:
data[vacuum_key]['set_values'] = set_values

View File

@ -399,8 +399,11 @@ def compare(trans_id, source_sid, source_did, source_scid,
if error_msg == gettext('Transaction ID not found in the session.'):
return make_json_response(success=0, errormsg=error_msg, status=404)
if not check_version_compatibility(source_sid, target_sid):
return not_implemented(errormsg=gettext("Version mismatch."))
# Server version compatibility check
status, msg = check_version_compatibility(source_sid, target_sid)
if not status:
return make_json_response(success=0, errormsg=msg, status=404)
comparison_result = []
@ -584,18 +587,24 @@ def check_version_compatibility(sid, tid):
driver = get_driver(PG_DEFAULT_DRIVER)
src_server = Server.query.filter_by(id=sid).first()
src_manager = driver.connection_manager(src_server.id)
src_conn = src_manager.connection()
tar_server = Server.query.filter_by(id=tid).first()
tar_manager = driver.connection_manager(tar_server.id)
tar_conn = tar_manager.connection()
if not (src_conn.connected() or src_conn.connected()):
return False, gettext('Server(s) disconnected.')
def get_round_val(x):
if x < 10000:
return x if x % 100 == 0 else x + 100 - x % 100
else:
return x if x % 10000 == 0 else x + 10000 - x % 10000
return x + 10000 - x % 10000
if get_round_val(src_manager.version) == \
get_round_val(tar_manager.version):
return True
return True, None
return False
return False, gettext('Source and Target database server must be of '
'the same major version.')

View File

@ -220,46 +220,54 @@ def directory_diff(source_dict, target_dict, ignore_keys=[], difference={}):
ignore_keys, difference)
elif type(source_dict[key]) is list:
tmp_target = None
for index in range(len(source_dict[key])):
source = copy.deepcopy(source_dict[key][index])
if type(source) is list:
# TODO
pass
elif type(source) is dict:
if 'name' in source or 'colname' in source:
if type(target_dict[key]) is list and len(
target_dict[key]) > 0:
tmp = None
tmp_target = copy.deepcopy(target_dict[key])
for item in tmp_target:
if (
'name' in item and
item['name'] == source['name']
) or (
'colname' in item and
item['colname'] == source['colname']
):
tmp = copy.deepcopy(item)
if tmp and source != tmp:
updated.append(copy.deepcopy(source))
tmp_target.remove(tmp)
elif tmp and source == tmp:
tmp_target.remove(tmp)
elif tmp is None:
tmp_list = list(filter(
lambda x: type(x) == list or type(x) == dict, source_dict[key]
))
if len(tmp_list) > 0:
for index in range(len(source_dict[key])):
source = copy.deepcopy(source_dict[key][index])
if type(source) is list:
# TODO
pass
elif type(source) is dict:
if 'name' in source or 'colname' in source:
if type(target_dict[key]) is list and len(
target_dict[key]) > 0:
tmp = None
tmp_target = copy.deepcopy(target_dict[key])
for item in tmp_target:
if (
'name' in item and
item['name'] == source['name']
) or (
'colname' in item and
item['colname'] == source[
'colname']
):
tmp = copy.deepcopy(item)
if tmp and source != tmp:
updated.append(copy.deepcopy(source))
tmp_target.remove(tmp)
elif tmp and source == tmp:
tmp_target.remove(tmp)
elif tmp is None:
added.append(source)
else:
added.append(source)
else:
added.append(source)
difference[key] = {}
difference[key]['added'] = added
difference[key]['changed'] = updated
elif target_dict[key] is None or \
(type(target_dict[key]) is list and
len(target_dict[key]) < index and
source != target_dict[key][index]):
difference[key] = source
elif type(target_dict[key]) is list and\
len(target_dict[key]) > index:
difference[key] = source
difference[key] = {}
difference[key]['added'] = added
difference[key]['changed'] = updated
elif target_dict[key] is None or \
(type(target_dict[key]) is list and
len(target_dict[key]) < index and
source != target_dict[key][index]):
difference[key] = source
elif type(target_dict[key]) is list and\
len(target_dict[key]) > index:
difference[key] = source
else:
target_dict[key] = source_dict[key]
if type(source) is dict and tmp_target and key in tmp_target and \
tmp_target[key] and len(tmp_target[key]) > 0:

View File

@ -50,7 +50,7 @@
padding: 5px 5px !important;
}
.slick-header-column.ui-state-default {
#schema-diff-grid .slick-header-column.ui-state-default {
height: 32px !important;
line-height: 25px !important;
}

View File

@ -69,6 +69,13 @@ let SchemaDiffSqlControl =
copyData() {
event.stopPropagation();
clipboard.copyTextToClipboard(this.model.get('diff_ddl'));
this.$el.find('.ddl-copy').text(gettext('Copied!'));
var self = this;
setTimeout(function() {
let $copy = self.$el.find('.ddl-copy');
if (!$copy.hasClass('d-none')) $copy.addClass('d-none');
$copy.text(gettext('Copy'));
}, 3000);
return false;
},
onFocus: function() {
@ -78,14 +85,6 @@ let SchemaDiffSqlControl =
if ($copy.hasClass('d-none')) $copy.removeClass('d-none');
},
onBlur: function() {
let $copy = this.$el.find('.ddl-copy');
if (!$(event.relatedTarget).hasClass('ddl-copy')) {
if (!$copy.hasClass('d-none')) $copy.addClass('d-none');
this.$el.find('.pgadmin-controls').first().removeClass('focused');
}
},
});
let SchemaDiffSelect2Control =
@ -302,7 +301,8 @@ let SchemaDiffHeaderView = Backform.Form.extend({
<button id="btn-filter" type="button" class="btn btn-sm btn-secondary"
title=""
accesskey=""
tabindex="0">
tabindex="0"
style="pointer-events: none;">
<i class="fa fa-filter sql-icon-lg" aria-hidden="true"></i>&nbsp;` + gettext('Filter') + `
</button>
<button id="btn-filter-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split"
@ -315,7 +315,7 @@ let SchemaDiffHeaderView = Backform.Form.extend({
'<ul class="dropdown-menu filter">',
'<li>',
'<a class="dropdown-item" id="btn-identical" href="#" tabindex="0">',
'<i class="identical fa fa-check" aria-hidden="true"></i>',
'<i class="identical fa fa-check visibility-hidden" aria-hidden="true"></i>',
'<span> ' + gettext('Identical') + ' </span>',
'</a>',
'</li>',

View File

@ -37,7 +37,7 @@ define('pgadmin.schemadiff', [
applies: ['tools'],
callback: 'show_schema_diff_tool',
priority: 1,
label: gettext('Schema Diff'),
label: gettext('Schema Diff (Beta)'),
enable: true,
}];
@ -85,7 +85,7 @@ define('pgadmin.schemadiff', [
})
.done(function(res) {
self.trans_id = res.data.schemaDiffTransId;
res.data.panel_title = 'Schema Diff'; //TODO: Set the panel title
res.data.panel_title = 'Schema Diff (Beta)'; //TODO: Set the panel title
// TODO: Following function is used to test the fetching of the
// databases this should be moved to server selection event later.
self.launch_schema_diff(res.data);

View File

@ -31,7 +31,7 @@ export default class SchemaDiffUI {
this.header = null;
this.trans_id = trans_id;
this.filters = ['Identical', 'Different', 'Source Only', 'Target Only'];
this.sel_filters = ['Identical', 'Different', 'Source Only', 'Target Only'];
this.sel_filters = ['Different', 'Source Only', 'Target Only'];
this.dataView = null;
this.grid = null,
this.selection = {};
@ -229,8 +229,12 @@ export default class SchemaDiffUI {
server_data['did'] = self.model.get('target_did');
server_data['database'] = data.database;
if (_.isUndefined(generated_script))
generated_script = 'BEGIN;' + '\n' + self.model.get('diff_ddl') + '\n' + 'END;';
if (_.isUndefined(generated_script)) {
generated_script = gettext('-- The generated script does not include the dependency resolution currently, so it may fail in case of dependency. \n');
generated_script += gettext('-- Please report an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new \n');
generated_script += 'BEGIN;' + '\n' + self.model.get('diff_ddl') + '\n' + 'END;';
}
let preferences = pgWindow.pgAdmin.Browser.get_preferences_for_module('schema_diff');
if (preferences.schema_diff_new_browser_tab) {