Improve logic to get the DDL statements as a part of the comparison. Fixes #5221

Fixed 23 issues related to schema diff.
This commit is contained in:
Akshay Joshi 2020-03-15 14:51:16 +05:30
parent 0b101d9efd
commit 44c0d76541
52 changed files with 951 additions and 921 deletions

View File

@ -20,3 +20,4 @@ Bug fixes
| `Issue #4237 <https://redmine.postgresql.org/issues/4237>`_ - Fix an issue where the user can not change the value of DateTime picker control using keyboard. | `Issue #4237 <https://redmine.postgresql.org/issues/4237>`_ - Fix an issue where the user can not change the value of DateTime picker control using keyboard.
| `Issue #4942 <https://redmine.postgresql.org/issues/4942>`_ - Fixed chrome driver download utility issue for Ubuntu. | `Issue #4942 <https://redmine.postgresql.org/issues/4942>`_ - Fixed chrome driver download utility issue for Ubuntu.
| `Issue #5143 <https://redmine.postgresql.org/issues/5143>`_ - Fix an accessibility issue to maximize the panel for all alertify dialog. | `Issue #5143 <https://redmine.postgresql.org/issues/5143>`_ - Fix an accessibility issue to maximize the panel for all alertify dialog.
| `Issue #5221 <https://redmine.postgresql.org/issues/5221>`_ - Improve logic to get the DDL statements as a part of the comparison.

View File

@ -220,7 +220,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
}) })
keys_to_ignore = ['oid', 'proowner', 'typnsp', 'xmin', 'prokind', keys_to_ignore = ['oid', 'proowner', 'typnsp', 'xmin', 'prokind',
'proisagg', 'pronamespace', 'proargdefaults'] 'proisagg', 'pronamespace', 'proargdefaults',
'prorettype', 'proallargtypes', 'proacl']
@property @property
def required_args(self): def required_args(self):
@ -1125,7 +1126,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
SQL = re.sub('\n{2,}', '\n\n', SQL) SQL = re.sub('\n{2,}', '\n\n', SQL)
return SQL return SQL
def _get_sql(self, gid, sid, did, scid, data, fnid=None, is_sql=False): def _get_sql(self, gid, sid, did, scid, data, fnid=None, is_sql=False,
is_schema_diff=False):
""" """
Generates the SQL statements to create/update the Function. Generates the SQL statements to create/update the Function.
@ -1239,6 +1241,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
for v in data['variables']['added']: for v in data['variables']['added']:
chngd_variables[v['name']] = v['value'] chngd_variables[v['name']] = v['value']
# In case of schema diff we don't want variables from
# old data
if not is_schema_diff:
for v in old_data['variables']: for v in old_data['variables']:
old_data['chngd_variables'][v['name']] = v['value'] old_data['chngd_variables'][v['name']] = v['value']
@ -1624,7 +1629,15 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
if data: if data:
if diff_schema: if diff_schema:
data['schema'] = diff_schema data['schema'] = diff_schema
status, sql = self._get_sql(gid, sid, did, scid, data, oid) status, sql = self._get_sql(gid, sid, did, scid, data, oid, False,
True)
# Check if return type is changed then we need to drop the
# function first and then recreate it.
drop_fun_sql = ''
if 'prorettypename' in data:
drop_fun_sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, fnid=oid, only_sql=True)
sql = drop_fun_sql + '\n' + sql
else: else:
if drop_sql: if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did, sql = self.delete(gid=gid, sid=sid, did=did,

View File

@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0'%}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}

View File

@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -28,7 +29,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -29,7 +30,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -27,7 +28,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}

View File

@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -28,7 +29,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -29,7 +30,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -19,7 +19,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -31,7 +32,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -18,7 +18,8 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% endfor %} {% endfor %}
{% endif -%} {% endif -%}
) )
RETURNS {{ o_data.prorettypename }} RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %} {% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %} LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }} LANGUAGE {{ o_data.lanname|qtLiteral }}
@ -27,7 +28,7 @@ CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if d
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} {% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} {% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}

View File

@ -11,8 +11,6 @@
import simplejson as json import simplejson as json
import re import re
import copy
import random
import pgadmin.browser.server_groups.servers.databases as database import pgadmin.browser.server_groups.servers.databases as database
from flask import render_template, request, jsonify, url_for, current_app from flask import render_template, request, jsonify, url_for, current_app
@ -25,11 +23,6 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
from .utils import BaseTableView from .utils import BaseTableView
from pgadmin.utils.preferences import Preferences from pgadmin.utils.preferences import Preferences
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints.foreign_key import utils as fkey_utils constraints.foreign_key import utils as fkey_utils
from .schema_diff_utils import SchemaDiffTableCompare from .schema_diff_utils import SchemaDiffTableCompare
@ -1212,8 +1205,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True
diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\ diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\
None None
schema_diff_table = kwargs['schema_diff_table'] if\
'schema_diff_table' in kwargs else None
if diff_data: if diff_data:
return self._fetch_sql(did, scid, tid, diff_data, json_resp) return self._fetch_sql(did, scid, tid, diff_data, json_resp)
@ -1239,25 +1230,9 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
if diff_schema: if diff_schema:
data['schema'] = diff_schema data['schema'] = diff_schema
if schema_diff_table:
data['orig_name'] = data['name']
data['name'] = 'schema_diff_temp_{0}'.format(
random.randint(1, 9999999))
sql, partition_sql = BaseTableView.get_reverse_engineered_sql(
self, did, scid, tid, main_sql, data, json_resp,
diff_partition_sql=True)
else:
sql, partition_sql = BaseTableView.get_reverse_engineered_sql( sql, partition_sql = BaseTableView.get_reverse_engineered_sql(
self, did, scid, tid, main_sql, data, json_resp) self, did, scid, tid, main_sql, data, json_resp)
if schema_diff_table:
# If partition tables have different partitions
sql += render_template(
"/".join([self.table_template_path, 'schema_diff.sql']),
conn=self.conn, data=data, partition_sql=partition_sql
)
return sql return sql
@BaseTableView.check_precondition @BaseTableView.check_precondition
@ -1625,7 +1600,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
return sql return sql
@BaseTableView.check_precondition @BaseTableView.check_precondition
def fetch_tables(self, sid, did, scid, tid=None, keys_to_remove=None): def fetch_tables(self, sid, did, scid, tid=None):
""" """
This function will fetch the list of all the tables This function will fetch the list of all the tables
and will be used by schema diff. and will be used by schema diff.
@ -1634,9 +1609,13 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
:param did: Database Id :param did: Database Id
:param scid: Schema Id :param scid: Schema Id
:param tid: Table Id :param tid: Table Id
:param keys_to_remove: Table columns to be removed from the dataset
:return: Table dataset :return: Table dataset
""" """
sub_modules = ['index', 'rule', 'trigger']
if self.manager.server_type == 'ppas' and \
self.manager.version >= 120000:
sub_modules.append('compound_trigger')
if tid: if tid:
status, data = self._fetch_properties(did, scid, tid) status, data = self._fetch_properties(did, scid, tid)
@ -1647,7 +1626,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
data = super(TableView, self).properties( data = super(TableView, self).properties(
0, sid, did, scid, tid, data, False 0, sid, did, scid, tid, data, False
) )
self.remove_keys_for_comparision(data, keys_to_remove)
return data return data
else: else:
@ -1667,7 +1646,17 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
0, sid, did, scid, row['oid'], data, False 0, sid, did, scid, row['oid'], data, False
) )
self.remove_keys_for_comparision(data, keys_to_remove) # Get sub module data of a specified table for object
# comparison
for module in sub_modules:
module_view = SchemaDiffRegistry.get_node_view(module)
if module_view.blueprint.server_type is None or \
self.manager.server_type in \
module_view.blueprint.server_type:
sub_data = module_view.fetch_objects_to_compare(
sid=sid, did=did, scid=scid, tid=row['oid'],
oid=None)
data[module] = sub_data
res[row['name']] = data res[row['name']] = data
return res return res

View File

@ -29,6 +29,8 @@ from pgadmin.utils import IS_PY2
from pgadmin.utils.compile_template_name import compile_template_path from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3 # If we are in Python3
@ -938,8 +940,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
return SQL return SQL
@check_precondition @check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None, def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
ignore_keys=False):
""" """
This function will fetch the list of all the triggers for This function will fetch the list of all the triggers for
specified schema id. specified schema id.
@ -969,14 +970,60 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
for row in triggers['rows']: for row in triggers['rows']:
status, data = self._fetch_properties(tid, row['oid']) status, data = self._fetch_properties(tid, row['oid'])
if status: if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data res[row['name']] = data
return res return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, CompoundTriggerView, 'table') SchemaDiffRegistry(blueprint.node_type, CompoundTriggerView, 'table')
CompoundTriggerView.register_node_view(blueprint) CompoundTriggerView.register_node_view(blueprint)

View File

@ -82,10 +82,10 @@ def get_foreign_keys(conn, tid, fkid=None, template_path=None):
coveringindex = search_coveringindex(conn, tid, cols) coveringindex = search_coveringindex(conn, tid, cols)
fk['coveringindex'] = coveringindex fk['coveringindex'] = coveringindex
if coveringindex: if coveringindex:
fk['autoindex'] = True fk['autoindex'] = False
fk['hasindex'] = True fk['hasindex'] = True
else: else:
fk['autoindex'] = False fk['autoindex'] = True
fk['hasindex'] = False fk['hasindex'] = False
return True, result['rows'] return True, result['rows']

View File

@ -26,9 +26,7 @@ from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER from config import PG_DEFAULT_DRIVER
from pgadmin.utils import IS_PY2 from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\ from pgadmin.tools.schema_diff.directory_compare import directory_diff
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.browser.server_groups.servers.databases.schemas. \ from pgadmin.browser.server_groups.servers.databases.schemas. \
tables.indexes import utils as index_utils tables.indexes import utils as index_utils
@ -830,11 +828,8 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
def get_sql_from_index_diff(self, sid, did, scid, tid, idx, data=None, def get_sql_from_index_diff(self, sid, did, scid, tid, idx, data=None,
diff_schema=None, drop_req=False): diff_schema=None, drop_req=False):
tmp_idx = idx sql = ''
schema = ''
if data: if data:
schema = self.schema
data['schema'] = self.schema data['schema'] = self.schema
data['nspname'] = self.schema data['nspname'] = self.schema
data['table'] = self.table data['table'] = self.table
@ -987,8 +982,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
) )
@check_precondition @check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None, def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
ignore_keys=False):
""" """
This function will fetch the list of all the indexes for This function will fetch the list of all the indexes for
specified schema id. specified schema id.
@ -996,6 +990,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
:param sid: Server Id :param sid: Server Id
:param did: Database Id :param did: Database Id
:param scid: Schema Id :param scid: Schema Id
:param oid: Index Id
:return: :return:
""" """
@ -1013,10 +1008,6 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
status, data = self._fetch_properties(did, tid, status, data = self._fetch_properties(did, tid,
row['oid']) row['oid'])
if status: if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data res[row['name']] = data
else: else:
status, data = self._fetch_properties(did, tid, status, data = self._fetch_properties(did, tid,
@ -1030,55 +1021,35 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
def ddl_compare(self, **kwargs): def ddl_compare(self, **kwargs):
""" """
This function will compare index properties and This function returns the DDL/DML statements based on the
return the difference of SQL comparison status.
:param kwargs:
:return:
""" """
src_sid = kwargs.get('source_sid') src_params = kwargs.get('source_params')
src_did = kwargs.get('source_did') tgt_params = kwargs.get('target_params')
src_scid = kwargs.get('source_scid') source = kwargs.get('source')
src_tid = kwargs.get('source_tid') target = kwargs.get('target')
src_oid = kwargs.get('source_oid') target_schema = kwargs.get('target_schema')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
tar_tid = kwargs.get('target_tid')
tar_oid = kwargs.get('target_oid')
comp_status = kwargs.get('comp_status') comp_status = kwargs.get('comp_status')
source = ''
target = ''
diff = '' diff = ''
if comp_status == 'source_only':
status, target_schema = self.get_schema(tar_sid, diff = self.get_sql_from_index_diff(sid=src_params['sid'],
tar_did, did=src_params['did'],
tar_scid scid=src_params['scid'],
) tid=src_params['tid'],
if not status: idx=source['oid'],
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
diff = self.get_sql_from_index_diff(sid=src_sid,
did=src_did, scid=src_scid,
tid=src_tid, idx=src_oid,
diff_schema=target_schema) diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.delete(gid=1, sid=tgt_params['sid'],
did=tgt_params['did'], scid=tgt_params['scid'],
tid=tgt_params['tid'], idx=target['oid'],
only_sql=True)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']: elif comp_status == 'different':
diff = self.delete(gid=1, sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_tid,
idx=tar_oid, only_sql=True)
else:
source = self.fetch_objects_to_compare(sid=src_sid, did=src_did,
scid=src_scid, tid=src_tid,
oid=src_oid)
target = self.fetch_objects_to_compare(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_tid,
oid=tar_oid)
if not (source or target):
return None
diff_dict = directory_diff( diff_dict = directory_diff(
source, target, ignore_keys=self.keys_to_ignore, source, target, ignore_keys=self.keys_to_ignore,
difference={} difference={}
@ -1102,19 +1073,19 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
create_req = True create_req = True
if create_req: if create_req:
diff = self.get_sql_from_index_diff(sid=tar_sid, diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],
did=tar_did, did=tgt_params['did'],
scid=tar_scid, scid=tgt_params['scid'],
tid=tar_tid, tid=tgt_params['tid'],
idx=tar_oid, idx=target['oid'],
diff_schema=target_schema, diff_schema=target_schema,
drop_req=True) drop_req=True)
else: else:
diff = self.get_sql_from_index_diff(sid=tar_sid, diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],
did=tar_did, did=tgt_params['did'],
scid=tar_scid, scid=tgt_params['scid'],
tid=tar_tid, tid=tgt_params['tid'],
idx=tar_oid, idx=target['oid'],
data=diff_dict) data=diff_dict)
return diff return diff

View File

@ -10,9 +10,10 @@
""" Implements Partitions Node """ """ Implements Partitions Node """
import re import re
import random
import simplejson as json import simplejson as json
import pgadmin.browser.server_groups.servers.databases.schemas as schema import pgadmin.browser.server_groups.servers.databases.schemas as schema
from flask import render_template, request from flask import render_template, request, current_app
from flask_babelex import gettext from flask_babelex import gettext
from pgadmin.browser.server_groups.servers.databases.schemas.utils \ from pgadmin.browser.server_groups.servers.databases.schemas.utils \
import DataTypeReader, VacuumSettings import DataTypeReader, VacuumSettings
@ -21,13 +22,9 @@ from pgadmin.utils.ajax import internal_server_error, \
from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \ from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \
import BaseTableView import BaseTableView
from pgadmin.browser.collection import CollectionNodeModule from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.utils.ajax import make_json_response, precondition_required from pgadmin.utils.ajax import make_json_response
from config import PG_DEFAULT_DRIVER
from pgadmin.browser.utils import PGChildModule from pgadmin.browser.utils import PGChildModule
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
@ -464,58 +461,57 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
@BaseTableView.check_precondition @BaseTableView.check_precondition
def get_sql_from_diff(self, **kwargs): def get_sql_from_diff(self, **kwargs):
""" """
This function will create sql on the basis the difference of 2 tables This function is used to get the DDL/DML statements for
partitions.
:param kwargs:
:return:
""" """
data = dict()
res = None
sid = kwargs['sid']
did = kwargs['did']
scid = kwargs['scid']
tid = kwargs['tid']
ptid = kwargs['ptid']
diff_data = kwargs['diff_data'] if 'diff_data' in kwargs else None
json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True
diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\
None
if diff_data: source_data = kwargs['source_data'] if 'source_data' in kwargs \
SQL = render_template("/".join([self.partition_template_path, else None
'properties.sql']), target_data = kwargs['target_data'] if 'target_data' in kwargs \
did=did, scid=scid, tid=tid, else None
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL, name = self.get_sql(did, scid, ptid, diff_data, res) # Store the original name and create a temporary name for
SQL = re.sub('\n{2,}', '\n\n', SQL) # the partitioned(base) table.
SQL = SQL.strip('\n') target_data['orig_name'] = target_data['name']
return SQL target_data['name'] = 'temp_partitioned_{0}'.format(
else: random.randint(1, 9999999))
main_sql = [] # For PG/EPAS 11 and above when we copy the data from original
# table to temporary table for schema diff, we will have to create
# a default partition to prevent the data loss.
target_data['default_partition_name'] = \
target_data['orig_name'] + '_default'
SQL = render_template("/".join([self.partition_template_path, # Copy the partition scheme from source to target.
'properties.sql']), if 'partition_scheme' in source_data:
did=did, scid=scid, tid=tid, target_data['partition_scheme'] = source_data['partition_scheme']
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0: partition_data = dict()
return gone(gettext( partition_data['name'] = target_data['name']
"The specified partitioned table could not be found.")) partition_data['schema'] = target_data['schema']
partition_data['partition_type'] = source_data['partition_type']
partition_data['default_partition_header'] = \
'-- Create a default partition to prevent the data loss.\n' \
'-- It helps when none of the partitions of a relation\n' \
'-- matches the inserted data.'
data = res['rows'][0] # Create temporary name for partitions
for item in source_data['partitions']:
item['temp_partition_name'] = 'partition_{0}'.format(
random.randint(1, 9999999))
if diff_schema: partition_data['partitions'] = source_data['partitions']
data['schema'] = diff_schema
data['parent_schema'] = diff_schema
return BaseTableView.get_reverse_engineered_sql(self, did, partition_sql = self.get_partitions_sql(partition_data,
scid, ptid, schema_diff=True)
main_sql, data,
False) return render_template(
"/".join([self.partition_template_path, 'partition_diff.sql']),
conn=self.conn, data=target_data, partition_sql=partition_sql,
partition_data=partition_data
)
@BaseTableView.check_precondition @BaseTableView.check_precondition
def detach(self, gid, sid, did, scid, tid, ptid): def detach(self, gid, sid, did, scid, tid, ptid):
@ -775,58 +771,25 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
def ddl_compare(self, **kwargs): def ddl_compare(self, **kwargs):
""" """
This function will compare index properties and This function returns the DDL/DML statements based on the
return the difference of SQL comparison status.
:param kwargs:
:return:
""" """
src_sid = kwargs.get('source_sid') tgt_params = kwargs.get('target_params')
src_did = kwargs.get('source_did') parent_source_data = kwargs.get('parent_source_data')
src_scid = kwargs.get('source_scid') parent_target_data = kwargs.get('parent_target_data')
src_tid = kwargs.get('source_tid')
src_oid = kwargs.get('source_oid')
tar_sid = kwargs.get('target_sid')
tar_did = kwargs.get('target_did')
tar_scid = kwargs.get('target_scid')
tar_tid = kwargs.get('target_tid')
tar_oid = kwargs.get('target_oid')
comp_status = kwargs.get('comp_status')
source = '' diff = self.get_sql_from_diff(sid=tgt_params['sid'],
target = '' did=tgt_params['did'],
diff = '' scid=tgt_params['scid'],
tid=tgt_params['tid'],
source_data=parent_source_data,
target_data=parent_target_data)
status, target_schema = self.get_schema_for_schema_diff(tar_sid, return diff + '\n'
tar_did,
tar_scid
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
diff = self.get_sql_from_diff(sid=src_sid,
did=src_did, scid=src_scid,
tid=src_tid, ptid=src_oid,
diff_schema=target_schema)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
SQL = render_template("/".join([self.partition_template_path,
'properties.sql']),
did=did, scid=scid, tid=tid,
ptid=ptid, datlastsysoid=self.datlastsysoid)
status, res = self.conn.execute_dict(SQL)
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:
self.cmd = 'delete'
diff = super(PartitionsView, self).get_delete_sql(res)
self.cmd = None
return diff
SchemaDiffRegistry(blueprint.node_type, PartitionsView, 'table') SchemaDiffRegistry(blueprint.node_type, PartitionsView, 'table')

View File

@ -28,6 +28,8 @@ from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils import IS_PY2 from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3 # If we are in Python3
if not IS_PY2: if not IS_PY2:
@ -520,7 +522,8 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
@check_precondition @check_precondition
def get_sql_from_diff(self, gid, sid, did, scid, tid, oid, data=None, def get_sql_from_diff(self, gid, sid, did, scid, tid, oid, data=None,
diff_schema=None, drop_sql=False): source_schema=None, diff_schema=None,
drop_sql=False):
if drop_sql: if drop_sql:
SQL = self.delete(gid=gid, sid=sid, did=did, SQL = self.delete(gid=gid, sid=sid, did=did,
@ -541,6 +544,11 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
SQL = '' SQL = ''
if data: if data:
if source_schema:
if 'statements' in data:
# Replace the source schema with the target schema
data['statements'] = data['statements'].replace(
source_schema, diff_schema)
old_data = res_data old_data = res_data
SQL = render_template( SQL = render_template(
"/".join([self.template_path, 'update.sql']), "/".join([self.template_path, 'update.sql']),
@ -548,6 +556,11 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
) )
else: else:
if diff_schema: if diff_schema:
if 'statements' in res_data:
# Replace the source schema with the target schema
res_data['statements'] = \
res_data['statements'].replace(
res_data['schema'], diff_schema)
res_data['schema'] = diff_schema res_data['schema'] = diff_schema
SQL = render_template("/".join( SQL = render_template("/".join(
@ -595,8 +608,7 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
) )
@check_precondition @check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None, def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
ignore_keys=False):
""" """
This function will fetch the list of all the rules for This function will fetch the list of all the rules for
specified schema id. specified schema id.
@ -628,13 +640,61 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
for row in rules['rows']: for row in rules['rows']:
status, data = self._fetch_properties(row['oid']) status, data = self._fetch_properties(row['oid'])
if status: if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data res[row['name']] = data
return res return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
source_schema=source['schema'],
diff_schema=target_schema,
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, RuleView, 'table') SchemaDiffRegistry(blueprint.node_type, RuleView, 'table')
RuleView.register_node_view(blueprint) RuleView.register_node_view(blueprint)

View File

@ -11,365 +11,96 @@
import copy import copy
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import internal_server_error from pgadmin.utils.ajax import internal_server_error
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\ from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
directory_diff are_dictionaries_identical
from pgadmin.tools.schema_diff.model import SchemaDiffModel
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
class SchemaDiffTableCompare(SchemaDiffObjectCompare): 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']
keys_to_ignore = ['oid', 'schema', 'vacuum_table', constraint_keys_to_ignore = ['relname', 'nspname', 'parent_tbl',
'vacuum_toast', 'edit_types', 'attnum', 'col_type', 'attrelid', 'adrelid', 'fknsp', 'confrelid',
'references', 'reltuples', 'rows_cnt'] 'references', 'refnsp', 'remote_schema']
keys_to_ignore_ddl_comp = ['oid', trigger_keys_to_ignore = ['xmin', 'tgrelid', 'tgfoid', 'tfunction',
'schema', 'tgqual', 'tgconstraint']
'columns', index_keys_to_ignore = ['relowner', 'indrelid']
'edit_types',
'primary_key',
'unique_constraint',
'exclude_constraint',
'check_constraint',
'foreign_key',
'reltuples',
'rows_cnt'
]
keys_to_remove = { keys_to_ignore = table_keys_to_ignore + constraint_keys_to_ignore \
'columns': ['relname', 'nspname', 'parent_tbl', 'attrelid', 'adrelid'], + trigger_keys_to_ignore + index_keys_to_ignore
'primary_key': ['oid'],
'unique_constraint': ['oid'],
'check_constraint': ['oid', 'nspname'],
'foreign_key': ['oid', 'fknsp', 'confrelid'],
'exclude_constraint': ['oid'],
'partitions': ['oid'],
}
keys_to_remove_ddl_comp = {
'columns': ['relname', 'nspname', 'parent_tbl', 'attrelid', 'adrelid'],
'check_constraint': ['nspname'],
'foreign_key': ['fknsp', 'confrelid']
}
def compare(self, **kwargs): def compare(self, **kwargs):
""" """
This function is used to compare all the table objects This function is used to compare all the table objects
from two different schemas. from two different schemas.
:return: Comparison Dictionary :param kwargs:
:return:
""" """
src_sid = kwargs.get('source_sid') source_params = {'sid': kwargs.get('source_sid'),
src_did = kwargs.get('source_did') 'did': kwargs.get('source_did'),
src_scid = kwargs.get('source_scid') 'scid': kwargs.get('source_scid')}
tar_sid = kwargs.get('target_sid') target_params = {'sid': kwargs.get('target_sid'),
tar_did = kwargs.get('target_did') 'did': kwargs.get('target_did'),
tar_scid = kwargs.get('target_scid') 'scid': kwargs.get('target_scid')}
sub_modules = ['index', 'rule', 'trigger']
source_tables = self.fetch_tables(sid=src_sid, did=src_did, status, target_schema = self.get_schema(**target_params)
scid=src_scid) if not status:
return internal_server_error(errormsg=target_schema)
target_tables = self.fetch_tables(sid=tar_sid, did=tar_did, source_tables = self.fetch_tables(**source_params)
scid=tar_scid) target_tables = self.fetch_tables(**target_params)
if self.manager.version >= 120000:
sub_modules.append('compound_trigger')
# If both the dict have no items then return None. # If both the dict have no items then return None.
if not (source_tables or target_tables) or ( if not (source_tables or target_tables) or (
len(source_tables) <= 0 and len(target_tables) <= 0): len(source_tables) <= 0 and len(target_tables) <= 0):
return None return None
src_server_type, tar_server_type = self.get_server_type(src_sid, return compare_dictionaries(self, source_params, target_params,
tar_sid) target_schema, source_tables,
for module in sub_modules: target_tables,
module_view = SchemaDiffRegistry.get_node_view(
module)
# Get sub module data for source tables
if module_view.blueprint.server_type is None or \
src_server_type in module_view.blueprint.server_type:
for key, val in source_tables.items():
source = module_view.fetch_objects_to_compare(
sid=src_sid,
did=src_did,
scid=src_scid,
tid=val['oid'],
oid=None,
ignore_keys=True
)
source_tables[key][module] = source
# Get sub module data for target tables
if module_view.blueprint.server_type is None or \
tar_server_type in module_view.blueprint.server_type:
for key, val in target_tables.items():
target = module_view.fetch_objects_to_compare(
sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=val['oid'],
oid=None,
ignore_keys=True
)
target_tables[key][module] = target
return compare_dictionaries(source_tables, target_tables,
self.node_type, self.node_type,
self.blueprint.COLLECTION_LABEL, self.blueprint.COLLECTION_LABEL,
self.keys_to_ignore) self.keys_to_ignore)
@staticmethod
def get_server_type(src_id, tar_id):
"""Get server types of source and target servers."""
driver = get_driver(PG_DEFAULT_DRIVER)
src_manager = driver.connection_manager(src_id)
tar_manager = driver.connection_manager(tar_id)
return src_manager.server_type, tar_manager.server_type
def ddl_compare(self, **kwargs): def ddl_compare(self, **kwargs):
""" """
This function will compare properties of 2 tables and This function will compare properties of 2 tables and
return the source DDL, target DDL and Difference of them. return the source DDL, target DDL and Difference of them.
:param kwargs:
:return:
""" """
source_params = {'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'),
'scid': kwargs.get('source_scid'),
'tid': kwargs.get('source_oid'),
'json_resp': False
}
src_sid = kwargs.get('source_sid') target_params = {'sid': kwargs.get('target_sid'),
src_did = kwargs.get('source_did') 'did': kwargs.get('target_did'),
src_scid = kwargs.get('source_scid') 'scid': kwargs.get('target_scid'),
src_oid = kwargs.get('source_oid') 'tid': kwargs.get('target_oid'),
tar_sid = kwargs.get('target_sid') 'json_resp': False
tar_did = kwargs.get('target_did') }
tar_scid = kwargs.get('target_scid')
tar_oid = kwargs.get('target_oid')
comp_status = kwargs.get('comp_status')
generate_script = False
if 'generate_script' in kwargs and kwargs['generate_script']: source = self.get_sql_from_table_diff(**source_params)
generate_script = True target = self.get_sql_from_table_diff(**target_params)
source = ''
target = ''
diff = ''
ignore_sub_modules = ['column', 'constraints']
src_server_type, tar_server_type = self.get_server_type(src_sid,
tar_sid)
status, target_schema = self.get_schema(tar_sid,
tar_did,
tar_scid
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
if not generate_script:
source = self.get_sql_from_table_diff(sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
json_resp=False)
diff = self.get_sql_from_table_diff(sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
diff_schema=target_schema,
json_resp=False)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
if not generate_script:
target = self.get_sql_from_table_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_oid,
json_resp=False)
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(
sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
keys_to_remove=self.keys_to_remove_ddl_comp
)
target = self.fetch_tables(
sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
keys_to_remove=self.keys_to_remove_ddl_comp
)
if self.manager.version < 100000:
ignore_sub_modules.append('partition')
if self.manager.version < 120000:
ignore_sub_modules.append('compound_trigger')
# In case of error return None
if not (source or target):
return None
diff_dict = directory_diff(
source, target, ignore_keys=self.keys_to_ignore_ddl_comp,
difference={}
)
# Column comparison
col_diff = self.table_col_ddl_comp(source, target)
diff_dict.update(col_diff)
# Constraint comparison
pk_diff = self.constraint_ddl_comp(source, target, diff_dict)
diff_dict.update(pk_diff)
diff_dict.update(self.parce_acl(source, target))
if not generate_script:
source = self.get_sql_from_table_diff(sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
json_resp=False)
target = self.get_sql_from_table_diff(sid=tar_sid,
did=tar_did,
scid=tar_scid,
tid=tar_oid,
json_resp=False)
diff = self.get_sql_from_table_diff(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
diff_data=diff_dict,
json_resp=False)
for module in self.blueprint.submodules:
if module.NODE_TYPE not in ignore_sub_modules:
module_view = SchemaDiffRegistry.get_node_view(
module.NODE_TYPE)
if module_view.blueprint.server_type and (
src_server_type not in
module_view.blueprint.server_type and
tar_server_type not in
module_view.blueprint.server_type
):
continue
if module_view.blueprint.server_type and (
(src_server_type in
module_view.blueprint.server_type and
tar_server_type not in
module_view.blueprint.server_type) or (
src_server_type not in
module_view.blueprint.server_type and
tar_server_type in
module_view.blueprint.server_type)
):
continue
result = module_view.compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid, source_tid=src_oid,
target_sid=tar_sid, target_did=tar_did,
target_scid=tar_scid, target_tid=tar_oid
)
if result and module.NODE_TYPE != 'partition':
child_diff = ''
for res in result:
if res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'different']:
source_oid = res['source_oid']
target_oid = res['target_oid']
else:
source_oid = res['oid']
target_oid = res['oid']
if res['status'] != \
SchemaDiffModel.COMPARISON_STATUS[
'identical']:
child_diff = module_view.ddl_compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid,
source_oid=source_oid,
source_tid=src_oid, target_sid=tar_sid,
target_did=tar_did, target_scid=tar_scid,
target_tid=tar_oid, target_oid=target_oid,
comp_status=res['status']
)
if child_diff:
diff += '\n' + child_diff
elif result:
# For partition module
identical = False
source_only = False
target_only = False
different = False
for res in result:
if res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'identical']:
identical = True
elif res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'source_only']:
source_only = True
elif res['status'] == \
SchemaDiffModel.COMPARISON_STATUS[
'target_only']:
target_only = True
else:
different = True
if identical:
pass
elif (source_only or target_only) and not different:
for res in result:
source_oid = res['oid']
target_oid = res['oid']
child_diff = module_view.ddl_compare(
source_sid=src_sid, source_did=src_did,
source_scid=src_scid,
source_oid=source_oid,
source_tid=src_oid, target_sid=tar_sid,
target_did=tar_did, target_scid=tar_scid,
target_tid=tar_oid, target_oid=target_oid,
comp_status=res['status']
)
if child_diff:
diff += child_diff
else:
diff = self.get_sql_from_table_diff(
sid=src_sid,
did=src_did,
scid=src_scid,
tid=src_oid,
diff_schema=target_schema,
json_resp=False,
schema_diff_table=True
)
else:
source = self.get_sql_from_table_diff(sid=src_sid, did=src_did,
scid=src_scid, tid=src_oid,
json_resp=False)
target = self.get_sql_from_table_diff(sid=tar_sid, did=tar_did,
scid=tar_scid, tid=tar_oid,
json_resp=False)
return {'source_ddl': source, return {'source_ddl': source,
'target_ddl': target, 'target_ddl': target,
'diff_ddl': diff 'diff_ddl': ''
} }
@staticmethod @staticmethod
def table_col_ddl_comp(source, target): def table_col_comp(source, target):
""" """
Table Column comparison Table Column comparison
:param source: Source columns :param source: Source columns
@ -413,17 +144,14 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
return different return different
@staticmethod @staticmethod
def constraint_ddl_comp(source_table, target_table, diff_dict): def table_constraint_comp(source_table, target_table):
""" """
Table Constraint DDL comparison Table Constraint DDL comparison
:param source: Source Table :param source_table: Source Table
:param target: Target Table :param target_table: Target Table
:return: Difference of constraints :return: Difference of constraints
""" """
different = {} different = {}
non_editable_keys = {}
columns_to_be_dropped = []
non_editable_keys = {'primary_key': ['col_count', non_editable_keys = {'primary_key': ['col_count',
'condeferrable', 'condeferrable',
'condeffered', 'condeffered',
@ -456,6 +184,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
if type(target_cols) is list and len( if type(target_cols) is list and len(
target_cols) > 0: target_cols) > 0:
tmp_src = copy.deepcopy(source) tmp_src = copy.deepcopy(source)
if 'oid' in tmp_src:
tmp_src.pop('oid') tmp_src.pop('oid')
tmp_tar = None tmp_tar = None
tmp = None tmp = None
@ -463,6 +192,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
if item['name'] == source['name']: if item['name'] == source['name']:
tmp_tar = copy.deepcopy(item) tmp_tar = copy.deepcopy(item)
tmp = copy.deepcopy(item) tmp = copy.deepcopy(item)
if 'oid' in tmp_tar:
tmp_tar.pop('oid') tmp_tar.pop('oid')
if tmp_tar and tmp_src != tmp_tar: if tmp_tar and tmp_src != tmp_tar:
tmp_updated = copy.deepcopy(source) tmp_updated = copy.deepcopy(source)
@ -474,6 +204,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
tmp_updated = None tmp_updated = None
break break
if tmp_updated: if tmp_updated:
if 'oid' in tmp:
tmp_updated['oid'] = tmp['oid'] tmp_updated['oid'] = tmp['oid']
updated.append(tmp_updated) updated.append(tmp_updated)
target_cols.remove(tmp) target_cols.remove(tmp)
@ -492,18 +223,109 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
return different return different
def remove_keys_for_comparision(self, data, keys=None): def get_sql_from_submodule_diff(self, source_params, target_params,
target_schema, source, target, diff_dict):
""" """
This function is used to remove specific keys from data This function returns the DDL/DML statements of the
submodules of table based on the comparison status.
:param source_params:
:param target_params:
:param target_schema:
:param source:
:param target:
:param diff_dict:
:return:
""" """
# Get the difference result for source and target columns
col_diff = self.table_col_comp(source, target)
diff_dict.update(col_diff)
keys_to_remove = keys if keys else self.keys_to_remove # Get the difference result for source and target constraints
pk_diff = self.table_constraint_comp(source, target)
diff_dict.update(pk_diff)
for p_key, p_val in keys_to_remove.items(): # Get the difference DDL/DML statements for table
if p_key in data and data[p_key] is not None \ target_params['diff_data'] = diff_dict
and len(data[p_key]) > 0: diff = self.get_sql_from_table_diff(**target_params)
for item in data[p_key]:
# Remove keys that should not be the part of comparision. ignore_sub_modules = ['column', 'constraints']
for key in p_val: if self.manager.version < 100000:
if key in item: ignore_sub_modules.append('partition')
item.pop(key) if self.manager.server_type == 'pg' or self.manager.version < 120000:
ignore_sub_modules.append('compound_trigger')
# Iterate through all the sub modules of the table
for module in self.blueprint.submodules:
if module.NODE_TYPE not in ignore_sub_modules:
module_view = \
SchemaDiffRegistry.get_node_view(module.NODE_TYPE)
if module.NODE_TYPE == 'partition' and \
('is_partitioned' in source and source['is_partitioned'])\
and ('is_partitioned' in target and
target['is_partitioned']):
target_ddl = module_view.ddl_compare(
target_params=target_params,
parent_source_data=source,
parent_target_data=target
)
diff += '\n' + target_ddl
elif module.NODE_TYPE != 'partition':
dict1 = copy.deepcopy(source[module.NODE_TYPE])
dict2 = copy.deepcopy(target[module.NODE_TYPE])
# 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)
# Keys that are available in source and missing in target.
added = dict1_keys - dict2_keys
for item in added:
source_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=dict1[item],
target=None,
target_schema=target_schema,
comp_status='source_only'
)
diff += '\n' + source_ddl
# Keys that are available in target and missing in source.
removed = dict2_keys - dict1_keys
for item in removed:
target_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=None,
target=dict2[item],
target_schema=target_schema,
comp_status='target_only'
)
diff += '\n' + target_ddl
# Keys that are available in both source and target.
for key in intersect_keys:
# Recursively Compare the two dictionary
if not are_dictionaries_identical(
dict1[key], dict2[key], self.keys_to_ignore):
diff_ddl = module_view.ddl_compare(
source_params=source_params,
target_params=target_params,
source=dict1[key],
target=dict2[key],
target_schema=target_schema,
comp_status='different',
parent_source_data=source,
parent_target_data=target
)
diff += '\n' + diff_ddl
return diff

View File

@ -9,7 +9,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% endif %} {% endif %}
{### Alter column type and collation ###} {### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%} {% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} {% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING: -- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however, -- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however,
@ -101,7 +101,7 @@ COMMENT ON COLUMN {{conn|qtIdent(data.schema, data.table, o_data.name)}}
{% endif %} {% endif %}
{### Update column variables ###} {### Update column variables ###}
{% if 'attoptions' in data and data.attoptions and data.attoptions|length > 0 %} {% if 'attoptions' in data and data.attoptions != None and data.attoptions|length > 0 %}
{% set variables = data.attoptions %} {% set variables = data.attoptions %}
{% if 'deleted' in variables and variables.deleted|length > 0 %} {% if 'deleted' in variables and variables.deleted|length > 0 %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}} ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}

View File

@ -6,17 +6,18 @@
{% if data.name and data.name != o_data.name %} {% if data.name and data.name != o_data.name %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}} ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}}; RENAME {{conn|qtIdent(o_data.name)}} TO {{conn|qtIdent(data.name)}};
{% endif %} {% endif %}
{### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} {% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING: -- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however, -- The SQL statement below would normally be used to alter the datatype for the {{o_data.name}} column, however,
-- the current datatype cannot be cast to the target datatype so this conversion cannot be made automatically. -- the current datatype cannot be cast to the target datatype so this conversion cannot be made automatically.
{% endif %} {% endif %}
{### Alter column type and collation ###} {% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %}ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%} {% if data.col_type_conversion is defined and data.col_type_conversion == False %} -- {% endif %} ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} TYPE {{ GET_TYPE.UPDATE_TYPE_SQL(conn, data, o_data) }}{% if data.collspcname and data.collspcname != o_data.collspcname %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
ALTER COLUMN {% if data.name %}{{conn|qtTypeIdent(data.name)}}{% else %}{{conn|qtTypeIdent(o_data.name)}}{% endif %} TYPE {{ GET_TYPE.UPDATE_TYPE_SQL(conn, data, o_data) }}{% if data.collspcname and data.collspcname != o_data.collspcname %}
COLLATE {{data.collspcname}}{% elif o_data.collspcname %} COLLATE {{o_data.collspcname}}{% endif %}; COLLATE {{data.collspcname}}{% elif o_data.collspcname %} COLLATE {{o_data.collspcname}}{% endif %};
{% endif %} {% endif %}
{### Alter column default value ###} {### Alter column default value ###}

View File

@ -9,7 +9,7 @@ ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
{% endif %} {% endif %}
{### Alter column type and collation ###} {### Alter column type and collation ###}
{% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname)%} {% if (data.cltype and data.cltype != o_data.cltype) or (data.attlen is defined and data.attlen != o_data.attlen) or (data.attprecision is defined and data.attprecision != o_data.attprecision) or (data.collspcname and data.collspcname != o_data.collspcname) or data.col_type_conversion is defined %}
{% if data.col_type_conversion is defined and data.col_type_conversion == False %} {% if data.col_type_conversion is defined and data.col_type_conversion == False %}
-- WARNING: -- WARNING:
-- The SQL statement below would normally be used to alter the datatype for the XXX column, however, -- The SQL statement below would normally be used to alter the datatype for the XXX column, however,

View File

@ -0,0 +1,22 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};

View File

@ -0,0 +1,24 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}{{partition_data.default_partition_header}}
CREATE TABLE IF NOT EXISTS {{conn|qtIdent(data.schema, data.default_partition_name)}} PARTITION OF {{conn|qtIdent(data.schema, data.name)}} DEFAULT;
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};

View File

@ -0,0 +1,22 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};

View File

@ -0,0 +1,24 @@
CREATE TABLE {{conn|qtIdent(data.schema, data.name)}} (
LIKE {{conn|qtIdent(data.schema, data.orig_name)}} INCLUDING ALL
) PARTITION BY {{ data.partition_scheme }};
{{partition_sql}}{{partition_data.default_partition_header}}
CREATE TABLE IF NOT EXISTS {{conn|qtIdent(data.schema, data.default_partition_name)}} PARTITION OF {{conn|qtIdent(data.schema, data.name)}} DEFAULT;
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %} {{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
{% if partition_data.partitions and partition_data.partitions|length > 0 %}
{% for part in partition_data.partitions %}
DROP TABLE IF EXISTS {{conn|qtIdent(data.schema, part.partition_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, part.temp_partition_name)}}
RENAME TO {{conn|qtIdent(part.partition_name)}};
{% endfor %}{% endif %}
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{data.orig_name}};

View File

@ -1,14 +0,0 @@
INSERT INTO {{conn|qtIdent(data.schema, data.name)}}(
{% if data.columns and data.columns|length > 0 %}
{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %})
SELECT {% if data.columns and data.columns|length > 0 %}{% for c in data.columns %}{{c.name}}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
FROM {{conn|qtIdent(data.schema, data.orig_name)}};
DROP TABLE {{conn|qtIdent(data.schema, data.orig_name)}};
{{partition_sql}}
ALTER TABLE {{conn|qtIdent(data.schema, data.name)}}
RENAME TO {{conn|qtIdent(data.orig_name)}};

View File

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

View File

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

View File

@ -3,9 +3,13 @@ ALTER TRIGGER {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname,
RENAME TO {{ conn|qtIdent(data.name) }}; RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined) and o_data.lanname == 'edbspl' and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires)) %} {% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined or data.is_constraint_trigger is defined) and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires or data.is_constraint_trigger != o_data.is_constraint_trigger)) %}
{% set or_flag = False %} {% set or_flag = False %}
{% if data.lanname == 'edbspl' or data.tfunction == 'Inline EDB-SPL' %}
CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }} CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% else %}
CREATE{% if data.is_constraint_trigger %} CONSTRAINT{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}
{% endif %}
{% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %} {% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %}
{% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %} {% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %}
{% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %} {% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %}
@ -16,23 +20,29 @@ CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %} {% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %} {% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}
{% endif %}{% else %}{% if data.evnt_update %} {% endif %}{% else %}{% if data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %} {% if or_flag %} OR {% endif %}UPDATE {% if data.columns|length > 0 %}OF {% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% endif %} {% endif %}
ON {{ conn|qtIdent(data.schema, data.table) }} ON {{ conn|qtIdent(data.schema, data.table) }}
{% if o_data.tgdeferrable %} {% if data.tgdeferrable %}
DEFERRABLE{% if data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% elif o_data.tgdeferrable %}
DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %} DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% endif %}{% if data.is_row_trigger is not defined %} {% endif %}{% if data.is_row_trigger is not defined %}
FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %} FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %}
FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %} FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %}
{% if o_data.whenclause %}
{% if data.whenclause %}
WHEN {{ data.whenclause }}
{% elif o_data.whenclause %}
WHEN {{ o_data.whenclause }} WHEN {{ o_data.whenclause }}
{% endif %} {% endif %}
{% if (data.tfunction is defined) %}
{% if (data.prosrc is not defined) %} EXECUTE PROCEDURE {{ data.tfunction }}{% if data.tgargs %}({{ data.tgargs }}){% else %}(){% endif%};
{{ o_data.prosrc }};
{% else %} {% else %}
{{ data.prosrc }}; EXECUTE PROCEDURE {{ o_data.tfunction }}{% if o_data.tgargs %}({{ o_data.tgargs }}){% else %}(){% endif%};
{% endif %} {% endif %}
{% if data.description is not defined and o_data.description %} {% if data.description is not defined and o_data.description %}

View File

@ -3,9 +3,13 @@ ALTER TRIGGER {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.nspname,
RENAME TO {{ conn|qtIdent(data.name) }}; RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
{% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined) and o_data.lanname == 'edbspl' and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires)) %} {% if ((data.prosrc is defined or data.is_row_trigger is defined or data.evnt_insert is defined or data.evnt_delete is defined or data.evnt_update is defined or data.fires is defined or data.is_constraint_trigger is defined) and (o_data.prosrc != data.prosrc or data.is_row_trigger != o_data.is_row_trigger or data.evnt_insert != o_data.evnt_insert or data.evnt_delete != o_data.evnt_delete or data.evnt_update != o_data.evnt_update or o_data.fires != data.fires or data.is_constraint_trigger != o_data.is_constraint_trigger)) %}
{% set or_flag = False %} {% set or_flag = False %}
{% if data.lanname == 'edbspl' or data.tfunction == 'Inline EDB-SPL' %}
CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }} CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% else %}
CREATE{% if data.is_constraint_trigger %} CONSTRAINT{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}
{% endif %}
{% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %} {% if data.fires is defined %}{{data.fires}} {% else %}{{o_data.fires}} {% endif %}{% if data.evnt_insert is not defined %}{% if o_data.evnt_insert %}INSERT{% set or_flag = True %}
{% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %} {% endif %}{% else %}{% if data.evnt_insert %}INSERT{% set or_flag = True %}{% endif %}{% endif %}{% if data.evnt_delete is not defined %}{% if o_data.evnt_delete %}
{% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %} {% if or_flag %} OR {% endif %}DELETE{% set or_flag = True %}
@ -16,24 +20,39 @@ CREATE OR REPLACE TRIGGER {{ conn|qtIdent(data.name) }}
{% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %} {% if or_flag %} OR {% endif %}TRUNCATE{% set or_flag = True %}{%endif %}{% endif %}{% if data.evnt_update is not defined %}{% if o_data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %} {% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}
{% endif %}{% else %}{% if data.evnt_update %} {% endif %}{% else %}{% if data.evnt_update %}
{% if or_flag %} OR {% endif %}UPDATE {% if o_data.columns|length > 0 %}OF {% for c in o_data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %} {% if or_flag %} OR {% endif %}UPDATE {% if data.columns|length > 0 %}OF {% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(c) }}{% endfor %}{% endif %}{% endif %}
{% endif %} {% endif %}
ON {{ conn|qtIdent(data.schema, data.table) }} ON {{ conn|qtIdent(data.schema, data.table) }}
{% if o_data.tgdeferrable %} {% if data.tgdeferrable %}
DEFERRABLE{% if data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% elif o_data.tgdeferrable %}
DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %} DEFERRABLE{% if o_data.tginitdeferred %} INITIALLY DEFERRED{% endif %}
{% endif %}{% if data.is_row_trigger is not defined %} {% endif %}{% if data.is_row_trigger is not defined %}
FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %} FOR EACH{% if o_data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% else %}
FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %} FOR EACH{% if data.is_row_trigger %} ROW{% else %} STATEMENT{% endif %} {% endif %}
{% if o_data.whenclause %}
{% if data.whenclause %}
WHEN {{ data.whenclause }}
{% elif o_data.whenclause %}
WHEN {{ o_data.whenclause }} WHEN {{ o_data.whenclause }}
{% endif %} {% endif %}
{%if data.tfunction == 'Inline EDB-SPL' %}
{% if (data.prosrc is not defined) %} {% if (data.prosrc is not defined) %}
{{ o_data.prosrc }}; {{ o_data.prosrc }};
{% else %} {% else %}
{{ data.prosrc }}; {{ data.prosrc }};
{% endif %} {% endif %}
{% else %}
{% if (data.tfunction is defined) %}
EXECUTE PROCEDURE {{ data.tfunction }}{% if data.tgargs %}({{ data.tgargs }}){% else %}(){% endif%};
{% else %}
EXECUTE PROCEDURE {{ o_data.tfunction }}{% if o_data.tgargs %}({{ o_data.tgargs }}){% else %}(){% endif%};
{% endif %}
{% endif %}
{% if data.description is not defined and o_data.description %} {% if data.description is not defined and o_data.description %}
COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }} COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspname, o_data.relname) }}

View File

@ -29,6 +29,8 @@ from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils import IS_PY2 from pgadmin.utils import IS_PY2
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.tools.schema_diff.directory_compare import directory_diff,\
parce_acl
# If we are in Python3 # If we are in Python3
if not IS_PY2: if not IS_PY2:
@ -794,7 +796,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
SQL, name = trigger_utils.get_sql( SQL, name = trigger_utils.get_sql(
self.conn, data, tid, oid, self.conn, data, tid, oid,
self.datlastsysoid, self.datlastsysoid,
self.blueprint.show_system_objects) self.blueprint.show_system_objects, True)
if not isinstance(SQL, (str, unicode)): if not isinstance(SQL, (str, unicode)):
return SQL return SQL
@ -932,8 +934,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
) )
@check_precondition @check_precondition
def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None, def fetch_objects_to_compare(self, sid, did, scid, tid, oid=None):
ignore_keys=False):
""" """
This function will fetch the list of all the triggers for This function will fetch the list of all the triggers for
specified schema id. specified schema id.
@ -963,14 +964,59 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
for row in triggers['rows']: for row in triggers['rows']:
status, data = self._fetch_properties(tid, row['oid']) status, data = self._fetch_properties(tid, row['oid'])
if status: if status:
if ignore_keys:
for key in self.keys_to_ignore:
if key in data:
del data[key]
res[row['name']] = data res[row['name']] = data
return res return res
def ddl_compare(self, **kwargs):
"""
This function returns the DDL/DML statements based on the
comparison status.
:param kwargs:
:return:
"""
src_params = kwargs.get('source_params')
tgt_params = kwargs.get('target_params')
source = kwargs.get('source')
target = kwargs.get('target')
target_schema = kwargs.get('target_schema')
comp_status = kwargs.get('comp_status')
diff = ''
if comp_status == 'source_only':
diff = self.get_sql_from_diff(gid=src_params['gid'],
sid=src_params['sid'],
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
drop_sql=True)
elif comp_status == 'different':
diff_dict = directory_diff(
source, target,
ignore_keys=self.keys_to_ignore, difference={}
)
diff_dict.update(parce_acl(source, target))
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
did=tgt_params['did'],
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
data=diff_dict)
return diff
SchemaDiffRegistry(blueprint.node_type, TriggerView, 'table') SchemaDiffRegistry(blueprint.node_type, TriggerView, 'table')
TriggerView.register_node_view(blueprint) TriggerView.register_node_view(blueprint)

View File

@ -141,7 +141,7 @@ def get_trigger_function_and_columns(conn, data, tid,
@get_template_path @get_template_path
def get_sql(conn, data, tid, trid, datlastsysoid, def get_sql(conn, data, tid, trid, datlastsysoid,
show_system_objects, template_path=None): show_system_objects, is_schema_diff=False, template_path=None):
""" """
This function will generate sql from model data. This function will generate sql from model data.
@ -151,6 +151,7 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
:param trid: Trigger ID :param trid: Trigger ID
:param datlastsysoid: :param datlastsysoid:
:param show_system_objects: Show System Object value True or False :param show_system_objects: Show System Object value True or False
:param is_schema_diff:
:param template_path: Optional template path :param template_path: Optional template path
:return: :return:
""" """
@ -173,6 +174,35 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
if 'name' not in data: if 'name' not in data:
name = data['name'] = old_data['name'] name = data['name'] = old_data['name']
drop_sql = ''
if is_schema_diff:
if 'table' not in data:
data['table'] = old_data['relname']
if 'schema' not in data:
data['schema'] = old_data['nspname']
# If any of the below key is present in data then we need to drop
# trigger and re-create it.
key_array = ['prosrc', 'is_row_trigger', 'evnt_insert',
'evnt_delete', 'evnt_update', 'fires', 'tgdeferrable',
'whenclause', 'tfunction', 'tgargs', 'columns',
'is_constraint_trigger', 'tginitdeferred']
is_drop_trigger = False
for key in key_array:
if key in data:
is_drop_trigger = True
break
if is_drop_trigger:
tmp_data = dict()
tmp_data['name'] = data['name']
tmp_data['nspname'] = old_data['nspname']
tmp_data['relname'] = old_data['relname']
drop_sql = render_template("/".join([template_path,
'delete.sql']),
data=tmp_data, conn=conn)
old_data = get_trigger_function_and_columns( old_data = get_trigger_function_and_columns(
conn, old_data, tid, show_system_objects) conn, old_data, tid, show_system_objects)
@ -182,6 +212,9 @@ def get_sql(conn, data, tid, trid, datlastsysoid,
"/".join([template_path, 'update.sql']), "/".join([template_path, 'update.sql']),
data=data, o_data=old_data, conn=conn data=data, o_data=old_data, conn=conn
) )
if is_schema_diff:
SQL = drop_sql + '\n' + SQL
else: else:
required_args = { required_args = {
'name': 'Name', 'name': 'Name',

View File

@ -588,6 +588,10 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
if not json_resp: if not json_resp:
display_comments = False display_comments = False
res_data = parse_rule_definition(res) res_data = parse_rule_definition(res)
# Update the correct table name for rules
if 'view' in res_data:
res_data['view'] = table
rules_sql += render_template("/".join( rules_sql += render_template("/".join(
[self.rules_template_path, 'create.sql']), [self.rules_template_path, 'create.sql']),
data=res_data, display_comments=display_comments) data=res_data, display_comments=display_comments)
@ -1298,11 +1302,12 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
status=200 status=200
) )
def get_partitions_sql(self, partitions): def get_partitions_sql(self, partitions, schema_diff=False):
""" """
This function will iterate all the partitions and create SQL. This function will iterate all the partitions and create SQL.
:param partitions: List of partitions :param partitions: List of partitions
:param schema_diff: If true then create sql accordingly.
""" """
sql = '' sql = ''
@ -1370,12 +1375,17 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
data=part_data, conn=self.conn data=part_data, conn=self.conn
) )
else: else:
# For schema diff we create temporary partitions to copy the
# data from original table to temporary table.
if schema_diff:
part_data['name'] = row['temp_partition_name']
partition_sql = render_template( partition_sql = render_template(
"/".join([self.partition_template_path, 'create.sql']), "/".join([self.partition_template_path, 'create.sql']),
data=part_data, conn=self.conn data=part_data, conn=self.conn
) )
sql += partition_sql + '\n' sql += partition_sql
return sql return sql

View File

@ -30,9 +30,9 @@ CREATE MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
{% if data.fillfactor or o_data.fillfactor %} {% if data.fillfactor or o_data.fillfactor %}
WITH( WITH(
{% if data.fillfactor %} {% if data.fillfactor %}
FILLFACTOR = {{ data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) or (o_data['vacuum_data'] is defined and o_data['vacuum_data']['changed']|length > 0) %},{% endif %} FILLFACTOR = {{ data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) %},{% endif %}
{% elif o_data.fillfactor %} {% elif o_data.fillfactor %}
FILLFACTOR = {{ o_data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) or (o_data['vacuum_data'] is defined and o_data['vacuum_data']['changed']|length > 0) %},{% endif %} FILLFACTOR = {{ o_data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) %},{% endif %}
{% endif %} {% endif %}
{% if data['vacuum_data']['changed']|length > 0 %} {% if data['vacuum_data']['changed']|length > 0 %}

View File

@ -30,12 +30,13 @@ CREATE MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
{% if data.fillfactor or o_data.fillfactor %} {% if data.fillfactor or o_data.fillfactor %}
WITH( WITH(
{% if data.fillfactor %} {% if data.fillfactor %}
FILLFACTOR = {{ data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) or (o_data['vacuum_data'] is defined and o_data['vacuum_data']['changed']|length > 0) %},{% endif %} FILLFACTOR = {{ data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) %},{% endif %}
{% elif o_data.fillfactor %} {% elif o_data.fillfactor %}
FILLFACTOR = {{ o_data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) or (o_data['vacuum_data'] is defined and o_data['vacuum_data']['changed']|length > 0) %},{% endif %} FILLFACTOR = {{ o_data.fillfactor }}{% if (data['vacuum_data'] is defined and data['vacuum_data']['changed']|length > 0) %},{% endif %}
{% endif %} {% endif %}
{% if data['vacuum_data']['changed']|length > 0 %} {% if data['vacuum_data']['changed']|length > 0 %}
{% for field in data['vacuum_data']['changed'] %} {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{{ '\n' }} {% for field in data['vacuum_data']['changed'] %} {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{{ '\n' }}{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
) )
@ -44,7 +45,6 @@ FILLFACTOR = {{ o_data.fillfactor }}{% if (data['vacuum_data'] is defined and da
{{ def }} {{ def }}
{% if data.with_data is defined %} {% if data.with_data is defined %}
WITH {{ 'DATA' if data.with_data else 'NO DATA' }}; WITH {{ 'DATA' if data.with_data else 'NO DATA' }};
{% elif o_data.with_data is defined %} {% elif o_data.with_data is defined %}
WITH {{ 'DATA' if o_data.with_data else 'NO DATA' }}; WITH {{ 'DATA' if o_data.with_data else 'NO DATA' }};
@ -128,8 +128,7 @@ ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET(
{% if('vacuum_table' in data and data['vacuum_table']['changed']|length > 0) %} {% if('vacuum_table' in data and data['vacuum_table']['changed']|length > 0) %}
ALTER MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }} SET( ALTER MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }} SET(
{% for field in data['vacuum_table']['changed'] %} {% for field in data['vacuum_table']['changed'] %}
{% if field.value != None %} {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %} {% if field.value != None %} {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{% endif %}
{% endif %}
{% endfor %} {% endfor %}
); );

View File

@ -19,7 +19,9 @@ ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
{% endif %} {% endif %}
{% if def and def != o_data.definition.rstrip(';') %} {% if def and def != o_data.definition.rstrip(';') %}
CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %}
WITH ({% if (data.check_option or o_data.check_option) %}check_option={{ data.check_option if data.check_option else o_data.check_option }}{{', ' }}{% endif %}security_barrier={{ data.security_barrier|lower if data.security_barrier is defined else o_data.security_barrier|default('false', 'true')|lower }}) WITH ({% if (data.check_option or o_data.check_option) %}check_option={{ data.check_option if data.check_option else o_data.check_option }}{{', ' }}{% endif %}security_barrier={{ data.security_barrier|lower if data.security_barrier is defined else o_data.security_barrier|default('false', 'true')|lower }})
{% endif %}
AS AS
{{ def }}; {{ def }};
{% else %} {% else %}

View File

@ -19,7 +19,9 @@ ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
{% endif %} {% endif %}
{% if def and def != o_data.definition.rstrip(';') %} {% if def and def != o_data.definition.rstrip(';') %}
CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %}
WITH ({% if (data.check_option or o_data.check_option) %}check_option={{ data.check_option if data.check_option else o_data.check_option }}{{', ' }}{% endif %}security_barrier={{ data.security_barrier|lower if data.security_barrier else o_data.security_barrier|default('false', 'true')|lower }}) WITH ({% if (data.check_option or o_data.check_option) %}check_option={{ data.check_option if data.check_option else o_data.check_option }}{{', ' }}{% endif %}security_barrier={{ data.security_barrier|lower if data.security_barrier else o_data.security_barrier|default('false', 'true')|lower }})
{% endif %}
AS AS
{{ def }}; {{ def }};
{% else %} {% else %}

View File

@ -1 +1,5 @@
ALTER VIEW public."testview_$%{}[]()&*^!@""'`\/#"
SET (security_barrier=true);
ALTER VIEW public."testview_$%{}[]()&*^!@""'`\/#"
SET (check_option=cascaded);
GRANT SELECT ON TABLE public."testview_$%{}[]()&*^!@""'`\/#" TO PUBLIC; GRANT SELECT ON TABLE public."testview_$%{}[]()&*^!@""'`\/#" TO PUBLIC;

View File

@ -3,10 +3,7 @@
-- DROP VIEW public."testview_$%{}[]()&*^!@""'`\/#"; -- DROP VIEW public."testview_$%{}[]()&*^!@""'`\/#";
CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#" CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#"
WITH ( AS
check_option=cascaded,
security_barrier=true
) AS
SELECT test_view_table.col1 SELECT test_view_table.col1
FROM test_view_table; FROM test_view_table;

View File

@ -1,4 +1,3 @@
CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#" CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#"
WITH (check_option=cascaded, security_barrier=true)
AS AS
SELECT * FROM test_view_table; SELECT * FROM test_view_table;

View File

@ -1 +1,5 @@
ALTER VIEW public."testview_$%{}[]()&*^!@""'`\/#"
SET (security_barrier=true);
ALTER VIEW public."testview_$%{}[]()&*^!@""'`\/#"
SET (check_option=cascaded);
GRANT SELECT ON TABLE public."testview_$%{}[]()&*^!@""'`\/#" TO PUBLIC; GRANT SELECT ON TABLE public."testview_$%{}[]()&*^!@""'`\/#" TO PUBLIC;

View File

@ -3,10 +3,7 @@
-- DROP VIEW public."testview_$%{}[]()&*^!@""'`\/#"; -- DROP VIEW public."testview_$%{}[]()&*^!@""'`\/#";
CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#" CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#"
WITH ( AS
check_option=cascaded,
security_barrier=true
) AS
SELECT test_view_table.col1 SELECT test_view_table.col1
FROM test_view_table; FROM test_view_table;

View File

@ -1,4 +1,3 @@
CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#" CREATE OR REPLACE VIEW public."testview_$%{}[]()&*^!@""'`\/#"
WITH (check_option=cascaded, security_barrier=true)
AS AS
SELECT * FROM test_view_table; SELECT * FROM test_view_table;

View File

@ -68,7 +68,6 @@ class SchemaDiffModule(PgAdminModule):
'schema_diff.connect_server', 'schema_diff.connect_server',
'schema_diff.connect_database', 'schema_diff.connect_database',
'schema_diff.get_server', 'schema_diff.get_server',
'schema_diff.generate_script',
'schema_diff.close' 'schema_diff.close'
] ]
@ -452,7 +451,7 @@ def compare(trans_id, source_sid, source_did, source_scid,
for node_name, node_view in all_registered_nodes.items(): for node_name, node_view in all_registered_nodes.items():
view = SchemaDiffRegistry.get_node_view(node_name) view = SchemaDiffRegistry.get_node_view(node_name)
if hasattr(view, 'compare'): if hasattr(view, 'compare'):
msg = "Comparing " + view.blueprint.COLLECTION_LABEL + " ..." msg = "Comparing " + view.blueprint.COLLECTION_LABEL
diff_model_obj.set_comparison_info(msg, total_percent) diff_model_obj.set_comparison_info(msg, total_percent)
# Update the message and total percentage in session object # Update the message and total percentage in session object
update_session_diff_transaction(trans_id, session_obj, update_session_diff_transaction(trans_id, session_obj,
@ -510,59 +509,6 @@ def poll(trans_id):
'diff_percentage': diff_percentage}) 'diff_percentage': diff_percentage})
@blueprint.route(
'/generate_script/<int:trans_id>/',
methods=["POST"],
endpoint="generate_script"
)
def generate_script(trans_id):
"""This function will generate the scripts for the selected objects."""
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
status, error_msg, diff_model_obj, session_obj = \
check_transaction_status(trans_id)
if error_msg == gettext('Transaction ID not found in the session.'):
return make_json_response(success=0, errormsg=error_msg, status=404)
source_sid = int(data['source_sid'])
source_did = int(data['source_did'])
source_scid = int(data['source_scid'])
target_sid = int(data['target_sid'])
target_did = int(data['target_did'])
target_scid = int(data['target_scid'])
diff_ddl = ''
for d in data['sel_rows']:
node_type = d['node_type']
source_oid = int(d['source_oid'])
target_oid = int(d['target_oid'])
comp_status = d['comp_status']
view = SchemaDiffRegistry.get_node_view(node_type)
if view and hasattr(view, 'ddl_compare') and \
comp_status != SchemaDiffModel.COMPARISON_STATUS['identical']:
sql = view.ddl_compare(source_sid=source_sid,
source_did=source_did,
source_scid=source_scid,
target_sid=target_sid,
target_did=target_did,
target_scid=target_scid,
source_oid=source_oid,
target_oid=target_oid,
comp_status=comp_status,
generate_script=True)
diff_ddl += sql['diff_ddl'] + '\n\n'
return ajax_response(
status=200,
response={'diff_ddl': diff_ddl}
)
@blueprint.route( @blueprint.route(
'/ddl_compare/<int:trans_id>/<int:source_sid>/<int:source_did>/' '/ddl_compare/<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>/'
@ -620,7 +566,11 @@ def check_version_compatibility(sid, tid):
tar_server = Server.query.filter_by(id=tid).first() tar_server = Server.query.filter_by(id=tid).first()
tar_manager = driver.connection_manager(tar_server.id) tar_manager = driver.connection_manager(tar_server.id)
tar_conn = tar_manager.connection()
if src_manager.server_type != tar_manager.server_type:
return False, gettext('Schema diff does not support the comparison '
'between Postgres Server and EDB Postgres '
'Advanced Server.')
if not (src_conn.connected() or src_conn.connected()): if not (src_conn.connected() or src_conn.connected()):
return False, gettext('Server(s) disconnected.') return False, gettext('Server(s) disconnected.')

View File

@ -15,8 +15,7 @@ from flask import render_template
from pgadmin.utils.driver import get_driver from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import internal_server_error from pgadmin.utils.ajax import internal_server_error
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\ from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries
directory_diff
from pgadmin.tools.schema_diff.model import SchemaDiffModel from pgadmin.tools.schema_diff.model import SchemaDiffModel
@ -68,10 +67,12 @@ class SchemaDiffObjectCompare:
'scid': kwargs.get('target_scid') 'scid': kwargs.get('target_scid')
} }
if 'source_tid' in kwargs: status, target_schema = self.get_schema(kwargs.get('target_sid'),
source_params['tid'] = kwargs['source_tid'] kwargs.get('target_did'),
if 'target_tid' in kwargs: kwargs.get('target_scid')
target_params['tid'] = kwargs['target_tid'] )
if not status:
return internal_server_error(errormsg=target_schema)
source = self.fetch_objects_to_compare(**source_params) source = self.fetch_objects_to_compare(**source_params)
@ -82,7 +83,8 @@ class SchemaDiffObjectCompare:
len(source) <= 0 and len(target) <= 0): len(source) <= 0 and len(target) <= 0):
return None return None
return compare_dictionaries(source, target, return compare_dictionaries(self, source_params, target_params,
target_schema, source, target,
self.node_type, self.node_type,
self.blueprint.COLLECTION_LABEL, self.blueprint.COLLECTION_LABEL,
self.keys_to_ignore) self.keys_to_ignore)
@ -93,13 +95,6 @@ class SchemaDiffObjectCompare:
return the difference of SQL return the difference of SQL
""" """
source = ''
target = ''
diff = ''
comp_status = kwargs.get('comp_status')
only_diff = False
generate_script = False
source_params = {'gid': 1, source_params = {'gid': 1,
'sid': kwargs.get('source_sid'), 'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'), 'did': kwargs.get('source_did'),
@ -114,98 +109,10 @@ class SchemaDiffObjectCompare:
'oid': kwargs.get('target_oid') 'oid': kwargs.get('target_oid')
} }
if 'source_tid' in kwargs:
source_params['tid'] = kwargs['source_tid']
only_diff = True
if 'target_tid' in kwargs:
target_params['tid'] = kwargs['target_tid']
only_diff = True
if 'generate_script' in kwargs and kwargs['generate_script']:
generate_script = True
source_params_adv = copy.deepcopy(source_params)
target_params_adv = copy.deepcopy(target_params)
del source_params_adv['gid']
del target_params_adv['gid']
status, target_schema = self.get_schema(kwargs.get('target_sid'),
kwargs.get('target_did'),
kwargs.get('target_scid')
)
if not status:
return internal_server_error(errormsg=target_schema)
if comp_status == SchemaDiffModel.COMPARISON_STATUS['source_only']:
if not generate_script:
source = self.get_sql_from_diff(**source_params)
source_params.update({
'diff_schema': target_schema
})
diff = self.get_sql_from_diff(**source_params)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['target_only']:
if not generate_script:
target = self.get_sql_from_diff(**target_params)
target_params.update(
{'drop_sql': True})
diff = self.get_sql_from_diff(**target_params)
elif comp_status == SchemaDiffModel.COMPARISON_STATUS['different']:
source = self.fetch_objects_to_compare(**source_params_adv)
target = self.fetch_objects_to_compare(**target_params_adv)
if not (source or target):
return None
diff_dict = directory_diff(source,
target,
ignore_keys=self.keys_to_ignore,
difference={}
)
diff_dict.update(self.parce_acl(source, target))
if not generate_script:
source = self.get_sql_from_diff(**source_params) source = self.get_sql_from_diff(**source_params)
target = self.get_sql_from_diff(**target_params) target = self.get_sql_from_diff(**target_params)
target_params.update(
{'data': diff_dict})
diff = self.get_sql_from_diff(**target_params)
else:
source = self.get_sql_from_diff(**source_params)
target = self.get_sql_from_diff(**target_params)
if only_diff:
return diff
return {'source_ddl': source, return {'source_ddl': source,
'target_ddl': target, 'target_ddl': target,
'diff_ddl': diff 'diff_ddl': ''
} }
@staticmethod
def parce_acl(source, target):
key = 'acl'
if 'datacl' in source:
key = 'datacl'
elif 'relacl' in source:
key = 'relacl'
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
return {key: diff}

View File

@ -15,11 +15,17 @@ from pgadmin.tools.schema_diff.model import SchemaDiffModel
count = 1 count = 1
def compare_dictionaries(source_dict, target_dict, node, node_label, def compare_dictionaries(view_object, source_params, target_params,
target_schema, source_dict, target_dict, node,
node_label,
ignore_keys=None): ignore_keys=None):
""" """
This function will compare the two dictionaries. This function will compare the two dictionaries.
:param view_object: View Object
:param source_params: Source Parameters
:param target_params: Target Parameters
:param target_schema: Target Schema Name
:param source_dict: First Dictionary :param source_dict: First Dictionary
:param target_dict: Second Dictionary :param target_dict: Second Dictionary
:param node: node type :param node: node type
@ -36,18 +42,43 @@ def compare_dictionaries(source_dict, target_dict, node, node_label,
dict2_keys = set(dict2.keys()) dict2_keys = set(dict2.keys())
intersect_keys = dict1_keys.intersection(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. # Keys that are available in source and missing in target.
source_only = [] source_only = []
added = dict1_keys - dict2_keys added = dict1_keys - dict2_keys
global count global count
for item in added: for item in added:
if node == 'table':
temp_src_params = copy.deepcopy(source_params)
temp_src_params['tid'] = source_dict[item]['oid']
temp_src_params['json_resp'] = False
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
temp_src_params.update({
'diff_schema': target_schema
})
diff_ddl = view_object.get_sql_from_table_diff(**temp_src_params)
else:
temp_src_params = copy.deepcopy(source_params)
temp_src_params['oid'] = source_dict[item]['oid']
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
temp_src_params.update({
'diff_schema': target_schema
})
diff_ddl = view_object.get_sql_from_diff(**temp_src_params)
source_only.append({ source_only.append({
'id': count, 'id': count,
'type': node, 'type': node,
'label': node_label, 'label': node_label,
'title': item, 'title': item,
'oid': source_dict[item]['oid'], 'oid': source_dict[item]['oid'],
'status': SchemaDiffModel.COMPARISON_STATUS['source_only'] 'status': SchemaDiffModel.COMPARISON_STATUS['source_only'],
'source_ddl': source_ddl,
'target_ddl': '',
'diff_ddl': diff_ddl
}) })
count += 1 count += 1
@ -55,13 +86,34 @@ def compare_dictionaries(source_dict, target_dict, node, node_label,
# Keys that are available in target and missing in source. # Keys that are available in target and missing in source.
removed = dict2_keys - dict1_keys removed = dict2_keys - dict1_keys
for item in removed: for item in removed:
if node == 'table':
temp_tgt_params = copy.deepcopy(target_params)
temp_tgt_params['tid'] = target_dict[item]['oid']
temp_tgt_params['json_resp'] = False
target_ddl = view_object.get_sql_from_table_diff(**temp_tgt_params)
if 'gid' in temp_tgt_params:
del temp_tgt_params['gid']
if 'json_resp' in temp_tgt_params:
del temp_tgt_params['json_resp']
diff_ddl = view_object.get_drop_sql(**temp_tgt_params)
else:
temp_tgt_params = copy.deepcopy(target_params)
temp_tgt_params['oid'] = target_dict[item]['oid']
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({ target_only.append({
'id': count, 'id': count,
'type': node, 'type': node,
'label': node_label, 'label': node_label,
'title': item, 'title': item,
'oid': target_dict[item]['oid'], 'oid': target_dict[item]['oid'],
'status': SchemaDiffModel.COMPARISON_STATUS['target_only'] 'status': SchemaDiffModel.COMPARISON_STATUS['target_only'],
'source_ddl': '',
'target_ddl': target_ddl,
'diff_ddl': diff_ddl
}) })
count += 1 count += 1
@ -69,13 +121,6 @@ def compare_dictionaries(source_dict, target_dict, node, node_label,
identical = [] identical = []
different = [] different = []
for key in intersect_keys: for key in intersect_keys:
# ignore the keys if available.
for ig_key in ignore_keys:
if ig_key in dict1[key]:
dict1[key].pop(ig_key)
if ig_key in dict2[key]:
dict2[key].pop(ig_key)
# Recursively Compare the two dictionary # Recursively Compare the two dictionary
if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys): if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys):
identical.append({ identical.append({
@ -89,6 +134,50 @@ def compare_dictionaries(source_dict, target_dict, node, node_label,
'status': SchemaDiffModel.COMPARISON_STATUS['identical'] 'status': SchemaDiffModel.COMPARISON_STATUS['identical']
}) })
else: 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={}
)
diff_dict.update(parce_acl(dict1[key], dict2[key]))
temp_src_params['tid'] = source_dict[key]['oid']
temp_tgt_params['tid'] = target_dict[key]['oid']
temp_src_params['json_resp'] = \
temp_tgt_params['json_resp'] = False
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
target_ddl = \
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)
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={}
)
diff_dict.update(parce_acl(dict1[key], dict2[key]))
temp_src_params['oid'] = source_dict[key]['oid']
temp_tgt_params['oid'] = target_dict[key]['oid']
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
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({ different.append({
'id': count, 'id': count,
'type': node, 'type': node,
@ -97,7 +186,10 @@ def compare_dictionaries(source_dict, target_dict, node, node_label,
'oid': source_dict[key]['oid'], 'oid': source_dict[key]['oid'],
'source_oid': source_dict[key]['oid'], 'source_oid': source_dict[key]['oid'],
'target_oid': target_dict[key]['oid'], 'target_oid': target_dict[key]['oid'],
'status': SchemaDiffModel.COMPARISON_STATUS['different'] 'status': SchemaDiffModel.COMPARISON_STATUS['different'],
'source_ddl': source_ddl,
'target_ddl': target_ddl,
'diff_ddl': diff_ddl
}) })
count += 1 count += 1
@ -143,13 +235,6 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
src_keys = set(source_dict.keys()) src_keys = set(source_dict.keys())
tar_keys = set(target_dict.keys()) tar_keys = set(target_dict.keys())
# ignore the keys if available.
for ig_key in ignore_keys:
if ig_key in src_keys:
source_dict.pop(ig_key)
if ig_key in target_dict:
target_dict.pop(ig_key)
# Keys that are available in source and missing in target. # Keys that are available in source and missing in target.
src_only = src_keys - tar_keys src_only = src_keys - tar_keys
# Keys that are available in target and missing in source. # Keys that are available in target and missing in source.
@ -167,6 +252,10 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys):
return False return False
for key in source_dict.keys(): for key in source_dict.keys():
# Continue if key is available in ignore_keys
if key in ignore_keys:
continue
if type(source_dict[key]) is dict: if type(source_dict[key]) is dict:
if not are_dictionaries_identical(source_dict[key], if not are_dictionaries_identical(source_dict[key],
target_dict[key], ignore_keys): target_dict[key], ignore_keys):
@ -235,20 +324,18 @@ def directory_diff(source_dict, target_dict, ignore_keys=[], difference={}):
# TODO # TODO
pass pass
elif type(source) is dict: elif type(source) is dict:
if 'name' in source or 'colname' in source: tmp_key_array = ['name', 'colname', 'argid']
if type(target_dict[key]) is list and len( for tmp_key in tmp_key_array:
target_dict[key]) > 0: if tmp_key in source:
if type(target_dict[key]) is list and \
len(target_dict[key]) > 0:
tmp = None tmp = None
tmp_target = copy.deepcopy(target_dict[key]) tmp_target = \
copy.deepcopy(target_dict[key])
for item in tmp_target: for item in tmp_target:
if ( if tmp_key in item and \
'name' in item and item[tmp_key] == \
item['name'] == source['name'] source[tmp_key]:
) or (
'colname' in item and
item['colname'] == source[
'colname']
):
tmp = copy.deepcopy(item) tmp = copy.deepcopy(item)
if tmp and source != tmp: if tmp and source != tmp:
updated.append(copy.deepcopy(source)) updated.append(copy.deepcopy(source))
@ -259,6 +346,7 @@ def directory_diff(source_dict, target_dict, ignore_keys=[], difference={}):
added.append(source) added.append(source)
else: else:
added.append(source) added.append(source)
difference[key] = {} difference[key] = {}
difference[key]['added'] = added difference[key]['added'] = added
difference[key]['changed'] = updated difference[key]['changed'] = updated
@ -271,7 +359,7 @@ def directory_diff(source_dict, target_dict, ignore_keys=[], difference={}):
len(target_dict[key]) > index: len(target_dict[key]) > index:
difference[key] = source difference[key] = source
else: else:
target_dict[key] = source_dict[key] difference[key] = source_dict[key]
if type(source) is dict and tmp_target and key in tmp_target and \ if type(source) is dict and tmp_target and key in tmp_target and \
tmp_target[key] and len(tmp_target[key]) > 0: tmp_target[key] and len(tmp_target[key]) > 0:
@ -286,6 +374,34 @@ def directory_diff(source_dict, target_dict, ignore_keys=[], difference={}):
else: else:
if source_dict[key] != target_dict[key]: 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] difference[key] = source_dict[key]
return difference return difference
def parce_acl(source, target):
key = 'acl'
if 'datacl' in source:
key = 'datacl'
elif 'relacl' in source:
key = 'relacl'
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
return {key: diff}

View File

@ -63,6 +63,8 @@ let SchemaDiffSqlControl =
}, },
render: function() { render: function() {
let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments); let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
obj.sqlCtrl.setOption('readOnly', true);
if(this.$el.find('.ddl-copy')) this.$el.find('.ddl-copy').on('click', this.copyData); if(this.$el.find('.ddl-copy')) this.$el.find('.ddl-copy').on('click', this.copyData);
return obj; return obj;
}, },

View File

@ -197,7 +197,6 @@ export default class SchemaDiffUI {
baseServerUrl = url_for('schema_diff.get_server', {'sid': self.selection['target_sid'], baseServerUrl = url_for('schema_diff.get_server', {'sid': self.selection['target_sid'],
'did': self.selection['target_did']}), 'did': self.selection['target_did']}),
sel_rows = self.grid ? self.grid.getSelectedRows() : [], sel_rows = self.grid ? self.grid.getSelectedRows() : [],
sel_rows_data = [],
url_params = self.selection, url_params = self.selection,
generated_script = undefined, generated_script = undefined,
open_query_tool, open_query_tool,
@ -261,45 +260,16 @@ export default class SchemaDiffUI {
}; };
if (sel_rows.length > 0) { if (sel_rows.length > 0) {
let script_body = '';
for (var row = 0; row < sel_rows.length; row++) { for (var row = 0; row < sel_rows.length; row++) {
let data = self.grid.getData().getItem(sel_rows[row]); let data = self.grid.getData().getItem(sel_rows[row]);
if(!_.isUndefined(data.diff_ddl)) {
if (data.type) { script_body += data.diff_ddl + '\n\n';
let tmp_data = {
'node_type': data.type,
'source_oid': parseInt(data.oid, 10),
'target_oid': parseInt(data.oid, 10),
'comp_status': data.status,
};
if(data.status && (data.status.toLowerCase() == 'different' || data.status.toLowerCase() == 'identical')) {
tmp_data['target_oid'] = data.target_oid;
}
sel_rows_data.push(tmp_data);
} }
} }
url_params['sel_rows'] = sel_rows_data; generated_script = script_header + 'BEGIN;' + '\n' + script_body + 'END;';
let baseUrl = url_for('schema_diff.generate_script', {'trans_id': self.trans_id});
$.ajax({
url: baseUrl,
method: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(url_params),
})
.done(function (res) {
if (res) {
generated_script = script_header + 'BEGIN;' + '\n' + res.diff_ddl + '\n' + 'END;';
}
open_query_tool(); open_query_tool();
})
.fail(function (xhr) {
self.raise_error_on_fail(gettext('Generate script error'), xhr);
$('#diff_fetching_data').addClass('d-none');
});
} else if (!_.isUndefined(self.model.get('diff_ddl'))) { } else if (!_.isUndefined(self.model.get('diff_ddl'))) {
open_query_tool(); open_query_tool();
} }
@ -448,6 +418,7 @@ export default class SchemaDiffUI {
render_grid_data(data) { render_grid_data(data) {
var self = this; var self = this;
self.grid.setSelectedRows([]); self.grid.setSelectedRows([]);
data.sort((a, b) => (a.label > b.label) ? 1 : (a.label === b.label) ? ((a.title > b.title) ? 1 : -1) : -1);
self.dataView.beginUpdate(); self.dataView.beginUpdate();
self.dataView.setItems(data); self.dataView.setItems(data);
self.dataView.setFilter(self.filter.bind(self)); self.dataView.setFilter(self.filter.bind(self));
@ -489,8 +460,13 @@ export default class SchemaDiffUI {
contentType: 'application/json', contentType: 'application/json',
}) })
.done(function (res) { .done(function (res) {
let msg = res.data.compare_msg + res.data.diff_percentage + '% completed'; let msg = res.data.compare_msg;
$('#diff_fetching_data').find('.schema-diff-busy-text').text(msg); if (res.data.diff_percentage != 100) {
msg = msg + ' (this may take a few minutes)...';
}
msg = msg + '<br>'+ res.data.diff_percentage + '% completed.';
$('#diff_fetching_data').find('.schema-diff-busy-text').html(msg);
}) })
.fail(function (xhr) { .fail(function (xhr) {
self.raise_error_on_fail(gettext('Poll error'), xhr); self.raise_error_on_fail(gettext('Poll error'), xhr);
@ -528,11 +504,9 @@ export default class SchemaDiffUI {
'diff_ddl': undefined, 'diff_ddl': undefined,
}); });
if(data.status && data.status.toLowerCase() == 'identical') {
var url_params = self.selection; var url_params = self.selection;
if(data.status && (data.status.toLowerCase() == 'different' || data.status.toLowerCase() == 'identical')) {
target_oid = data.target_oid; target_oid = data.target_oid;
}
url_params['trans_id'] = self.trans_id; url_params['trans_id'] = self.trans_id;
url_params['source_oid'] = source_oid; url_params['source_oid'] = source_oid;
@ -559,6 +533,15 @@ export default class SchemaDiffUI {
$('#ddl_comp_fetching_data').addClass('d-none'); $('#ddl_comp_fetching_data').addClass('d-none');
}, },
}); });
} else {
self.model.set({
'source_ddl': data.source_ddl,
'target_ddl': data.target_ddl,
'diff_ddl': data.diff_ddl,
});
self.footer.render();
}
} }
render() { render() {

View File

@ -142,16 +142,10 @@ class SchemaDiffTestCase(BaseTestGenerator):
file_obj = open(diff_file, 'a') file_obj = open(diff_file, 'a')
for diff in response_data['data']: for diff in response_data['data']:
if diff['type'] in self.nodes: if diff['type'] in self.nodes and diff['status'] == 'Identical':
src_obj_oid = tar_obj_oid = None
if diff['status'] == 'Source Only' or\
diff['status'] == 'Target Only':
src_obj_oid = tar_obj_oid = diff['oid']
elif diff['status'] == 'Different':
src_obj_oid = diff['source_oid'] src_obj_oid = diff['source_oid']
tar_obj_oid = diff['target_oid'] tar_obj_oid = diff['target_oid']
if src_obj_oid is not None and tar_obj_oid is not None:
if src_obj_oid is not None:
url = 'schema_diff/ddl_compare/{0}/{1}/{2}/{3}/{4}/{5}/' \ url = 'schema_diff/ddl_compare/{0}/{1}/{2}/{3}/{4}/{5}/' \
'{6}/{7}/{8}/{9}/{10}/'.format(self.trans_id, '{6}/{7}/{8}/{9}/{10}/'.format(self.trans_id,
self.server_id, self.server_id,