diff --git a/docs/en_US/images/schema_diff_DDL_comparison.png b/docs/en_US/images/schema_diff_DDL_comparison.png index 11fd5bfa6..0c005b361 100644 Binary files a/docs/en_US/images/schema_diff_DDL_comparison.png and b/docs/en_US/images/schema_diff_DDL_comparison.png differ diff --git a/docs/en_US/images/schema_diff_compare_button.png b/docs/en_US/images/schema_diff_compare_button.png index c3f8d5092..7bdf534ce 100644 Binary files a/docs/en_US/images/schema_diff_compare_button.png and b/docs/en_US/images/schema_diff_compare_button.png differ diff --git a/docs/en_US/images/schema_diff_comparison_results.png b/docs/en_US/images/schema_diff_comparison_results.png index cb6584150..f0aaa6b43 100644 Binary files a/docs/en_US/images/schema_diff_comparison_results.png and b/docs/en_US/images/schema_diff_comparison_results.png differ diff --git a/docs/en_US/images/schema_diff_dialog.png b/docs/en_US/images/schema_diff_dialog.png index f654de29d..ca4500eae 100644 Binary files a/docs/en_US/images/schema_diff_dialog.png and b/docs/en_US/images/schema_diff_dialog.png differ diff --git a/docs/en_US/images/schema_diff_filter_option.png b/docs/en_US/images/schema_diff_filter_option.png index 69bd958f9..10b0a61dd 100644 Binary files a/docs/en_US/images/schema_diff_filter_option.png and b/docs/en_US/images/schema_diff_filter_option.png differ diff --git a/docs/en_US/images/schema_diff_generate_script_query_editor.png b/docs/en_US/images/schema_diff_generate_script_query_editor.png index eea3ff19f..35675298e 100644 Binary files a/docs/en_US/images/schema_diff_generate_script_query_editor.png and b/docs/en_US/images/schema_diff_generate_script_query_editor.png differ diff --git a/docs/en_US/release_notes_4_28.rst b/docs/en_US/release_notes_4_28.rst index 0aeb28373..8c86fddba 100644 --- a/docs/en_US/release_notes_4_28.rst +++ b/docs/en_US/release_notes_4_28.rst @@ -12,6 +12,7 @@ New features | `Issue #3318 `_ - Added support to download utility files at the client-side. | `Issue #4230 `_ - Added support to rename query tool and debugger tabs title. | `Issue #4232 `_ - Added tab title placeholder for Query Tool, View/Edit Data, and Debugger. +| `Issue #5891 `_ - Added support to compare schemas and databases in schema diff. Housekeeping ************ diff --git a/docs/en_US/schema_diff.rst b/docs/en_US/schema_diff.rst index 5bf7047a0..f6782ebef 100644 --- a/docs/en_US/schema_diff.rst +++ b/docs/en_US/schema_diff.rst @@ -5,7 +5,7 @@ ******************** **Schema Diff** is a feature that allows you to compare objects between -two databases. Use the *Tools* menu to access Schema Diff. +two databases or two schemas. Use the *Tools* menu to access Schema Diff. The Schema Diff feature allows you to: @@ -18,16 +18,21 @@ The Schema Diff feature allows you to: * Generate synchronization scripts. -**Note** - The source and target database servers must be of the same major -version. +**Note:** -Click on *Schema Diff* under the *Tools* menu to open a selection panel. Choose -the source and target servers, and databases that will be -compared. After selecting the objects, click on the *Compare* button. + * The source and target database servers must be of the same major version. + + * If you compare two **schemas** then dependencies won't be resolved. + +Click on *Schema Diff* under the *Tools* menu to open a selection panel. +To compare **databases** choose the source and target servers, and databases. +To compare **schemas** choose the source and target servers, databases, and schemas. +After selecting the objects, click on the *Compare* button. You can open multiple copies of *Schema Diff* in individual tabs simultaneously. To close a copy of Schema Diff, click the *X* in the -upper-right hand corner of the tab bar. +upper-right hand corner of the tab bar. You can rename the panel title by +right-clicking and select the "Rename Panel" option. .. image:: images/schema_diff_dialog.png :alt: schema diff dialog diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py index d87f8101b..13535ee51 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py @@ -700,6 +700,7 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare): json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) SQL = render_template("/".join([self.template_path, self._PROPERTIES_SQL]), @@ -711,6 +712,8 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare): return gone(self.not_found_error_msg()) data = res['rows'][0] + if target_schema: + data['schema'] = target_schema SQL = render_template("/".join([self.template_path, self._CREATE_SQL]), @@ -811,14 +814,20 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.get_sql(gid=gid, sid=sid, data=data, scid=scid, coid=oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, coid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py index 3178c06fc..9c60cdf57 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -721,6 +721,7 @@ AND relkind != 'c'))""" json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) SQL = render_template("/".join([self.template_path, self._PROPERTIES_SQL]), @@ -732,6 +733,8 @@ AND relkind != 'c'))""" return gone(self.not_found_error_msg()) data = res['rows'][0] + if target_schema: + data['basensp'] = target_schema # Get Type Length and Precision data.update(self._parse_type(data['fulltype'])) @@ -949,8 +952,11 @@ AND relkind != 'c'))""" oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.get_sql(gid=gid, sid=sid, scid=scid, data=data, doid=oid, is_schema_diff=True) @@ -958,6 +964,9 @@ AND relkind != 'c'))""" if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, doid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py index 807b11e52..1afe38fa3 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py @@ -827,6 +827,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader, json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) status, data = self._fetch_properties(gid, sid, did, scid, foid, inherits=True) @@ -841,6 +842,8 @@ class ForeignTableView(PGChildNodeView, DataTypeReader, col_data.append(c) data['columns'] = col_data + if target_schema: + data['basensp'] = target_schema # Parse Privileges if 'acl' in data: @@ -1515,6 +1518,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader, oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid, @@ -1524,6 +1528,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader, if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, foid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, foid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, foid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/__init__.py index b807107b4..bd59cac5a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_configurations/__init__.py @@ -904,6 +904,7 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare): :param json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) try: sql = render_template( @@ -928,6 +929,22 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare): "FTS Configuration node.") ) + # Used for schema diff tool + if target_schema: + data = {'schema': scid} + # Fetch schema name from schema oid + sql = render_template("/".join([self.template_path, + 'schema.sql']), + data=data, + conn=self.conn, + ) + + status, schema = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=schema) + + res = res.replace(schema, target_schema) + if not json_resp: return res @@ -1013,6 +1030,7 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid, @@ -1021,6 +1039,9 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py index c6a1cac2f..b99987b7a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_dictionaries/__init__.py @@ -800,6 +800,7 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare): :param json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) sql = render_template( "/".join([self.template_path, self._PROPERTIES_SQL]), @@ -843,6 +844,8 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare): # Replace schema oid with schema name res['rows'][0]['schema'] = schema + if target_schema: + res['rows'][0]['schema'] = target_schema sql = render_template("/".join([self.template_path, self._CREATE_SQL]), @@ -938,6 +941,7 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid, @@ -946,6 +950,9 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, dcid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/__init__.py index 13d35a28d..b6bc88166 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_parsers/__init__.py @@ -849,6 +849,7 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare): :param json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) try: sql = render_template( @@ -874,6 +875,22 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare): ) ) + # Used for schema diff tool + if target_schema: + data = {'schema': scid} + # Fetch schema name from schema oid + sql = render_template("/".join([self.template_path, + 'schema.sql']), + data=data, + conn=self.conn, + ) + + status, schema = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=schema) + + res = res.replace(schema, target_schema) + if not json_resp: return res @@ -959,6 +976,7 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid, @@ -967,6 +985,9 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, pid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py index d507bf3c3..0b831e753 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py @@ -729,6 +729,7 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare): :param json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) sql = render_template( "/".join([self.template_path, 'sql.sql']), @@ -751,6 +752,22 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare): "FTS Template node.") ) + # Used for schema diff tool + if target_schema: + data = {'schema': scid} + # Fetch schema name from schema oid + sql = render_template("/".join([self.template_path, + 'schema.sql']), + data=data, + conn=self.conn, + ) + + status, schema = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=schema) + + res = res.replace(schema, target_schema) + if not json_resp: return res @@ -832,6 +849,7 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid, @@ -840,6 +858,9 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, tid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py index a05d91582..9ca0b0397 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py @@ -1085,6 +1085,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): json_resp: """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) resp_data = self._fetch_properties(gid, sid, did, scid, fnid) # Most probably this is due to error @@ -1128,6 +1129,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): status, res = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) + elif target_schema: + res['rows'][0]['nspname'] = target_schema # Add newline and tab before each argument to format name_with_default_args = self.qtIdent( @@ -1173,6 +1176,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): status, res = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) + elif target_schema: + res['rows'][0]['nspname'] = target_schema + resp_data['pronamespace'] = target_schema # Add newline and tab before each argument to format name_with_default_args = self.qtIdent( @@ -1389,7 +1395,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c', 'provolatile', 'proisstrict', 'prosecdef', 'proparallel', 'procost', 'proleakproof', - 'arguments', 'prorows', 'prosupportfunc'] + 'arguments', 'prorows', 'prosupportfunc', + 'prorettypename'] data['change_func'] = False for arg in fun_change_args: @@ -1828,8 +1835,11 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema status, sql = self._get_sql(gid=gid, sid=sid, did=did, scid=scid, data=data, fnid=oid, is_sql=False, is_schema_diff=True) @@ -1843,6 +1853,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, fnid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, fnid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, fnid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py index 96522d6a5..80aab1c14 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py @@ -582,8 +582,12 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare): pkgid = kwargs.get('pkgid', None) sqltab = kwargs.get('sqltab', False) is_schema_diff = kwargs.get('is_schema_diff', None) + target_schema = kwargs.get('target_schema', None) - data['schema'] = self.schema + if target_schema: + data['schema'] = target_schema + else: + data['schema'] = self.schema if pkgid is not None and not sqltab: return self.get_sql_with_pkgid(scid, pkgid, data, is_schema_diff) @@ -687,6 +691,7 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare): """ is_schema_diff = kwargs.get('is_schema_diff', None) json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) try: sql = render_template( @@ -719,10 +724,13 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare): res['rows'][0].setdefault(row['deftype'], []).append(priv) result = res['rows'][0] + if target_schema: + result['schema'] = target_schema sql, name = self.getSQL(data=result, scid=scid, pkgid=pkgid, sqltab=True, - is_schema_diff=is_schema_diff) + is_schema_diff=is_schema_diff, + target_schema=target_schema) # Most probably this is due to error if not isinstance(sql, str): @@ -847,13 +855,20 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.getSQL(data=data, scid=scid, pkgid=oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, pkgid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pkgid=oid, + is_schema_diff=True, json_resp=False, + target_schema=target_schema) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pkgid=oid, is_schema_diff=True, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py index a9dd7d2e5..e2d562125 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py @@ -696,6 +696,7 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): json_resp: json response or plain text response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) sql = render_template( "/".join([self.template_path, self._PROPERTIES_SQL]), @@ -725,6 +726,8 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): row['cycled'] = rset1['rows'][0]['is_cycled'] result = res['rows'][0] + if target_schema: + result['schema'] = target_schema result = self._formatter(result, scid, seid) sql, name = self.get_SQL(gid, sid, did, result, scid) @@ -942,13 +945,19 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.get_SQL(gid, sid, did, data, scid, oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, seid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, seid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, seid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py index 6688dc34c..d0c97e17c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py @@ -181,6 +181,8 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare): {'get': 'get_target_objects'}] }) + keys_to_ignore = ['oid', 'oid-2', 'schema', 'synobjschema'] + def check_precondition(f): """ This function will behave as a decorator which will checks @@ -666,6 +668,7 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare): json_resp: """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) SQL = render_template("/".join([self.template_path, self._PROPERTIES_SQL]), @@ -679,6 +682,9 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare): else: return gone(self.not_found_error_msg()) + if target_schema: + data['schema'] = target_schema + SQL = render_template("/".join([self.template_path, self._CREATE_SQL]), data=data, conn=self.conn, comment=True) @@ -768,13 +774,19 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.get_sql(gid, sid, data, scid, oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, syid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, syid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, syid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py index f5e309877..6da40f9f5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py @@ -1245,12 +1245,13 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings, This function will create sql on the basis the difference of 2 tables """ data = dict() - res = None did = kwargs['did'] scid = kwargs['scid'] tid = kwargs['tid'] diff_data = kwargs['diff_data'] if 'diff_data' in kwargs else None json_resp = kwargs['json_resp'] if 'json_resp' in kwargs else True + target_schema = kwargs['target_schema'] \ + if 'target_schema' in kwargs else None if diff_data: return self._fetch_sql(did, scid, tid, diff_data, json_resp) @@ -1275,6 +1276,9 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings, # Update autovacuum properties self.update_autovacuum_properties(data) + if target_schema: + data['schema'] = target_schema + sql, partition_sql = BaseTableView.get_reverse_engineered_sql( self, did=did, scid=scid, tid=tid, main_sql=main_sql, data=data, json_resp=json_resp) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/compound_triggers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/compound_triggers/__init__.py index ace0e563d..0525a0e91 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/compound_triggers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/compound_triggers/__init__.py @@ -876,6 +876,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: sql, name = compound_trigger_utils.get_sql(self.conn, @@ -913,17 +914,21 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare): data['columns'] = self._column_details(tid, columns) data = trigger_definition(data) - sql = self._check_and_add_compound_trigger(tid, data) + sql = self._check_and_add_compound_trigger(tid, data, + target_schema) return sql - def _check_and_add_compound_trigger(self, tid, data): + def _check_and_add_compound_trigger(self, tid, data, target_schema): """ This get compound trigger and check for disable. :param tid: Table Id. :param data: Data. - :param diff_schema: schema diff check. + :param target_schema: schema diff check. """ + if target_schema: + data['schema'] = target_schema + sql, name = compound_trigger_utils.get_sql(self.conn, data, tid, @@ -989,6 +994,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare): source = kwargs.get('source') target = kwargs.get('target') comp_status = kwargs.get('comp_status') + target_schema = kwargs.get('target_schema', None) diff = '' if comp_status == 'source_only': @@ -997,7 +1003,8 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare): did=src_params['did'], scid=src_params['scid'], tid=src_params['tid'], - oid=source['oid']) + oid=source['oid'], + target_schema=target_schema) elif comp_status == 'target_only': diff = self.get_sql_from_diff(gid=tgt_params['gid'], sid=tgt_params['sid'], diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py index fdb79ca04..94c350cf3 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py @@ -844,8 +844,9 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare): tid = kwargs.get('tid') idx = kwargs.get('idx') data = kwargs.get('data', None) - create_mode = kwargs.get('create_mode', None) + target_schema = kwargs.get('target_schema', None) drop_req = kwargs.get('drop_req', False) + sql = '' if data: @@ -859,9 +860,9 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare): sql = sql.strip('\n').strip(' ') - elif create_mode: + elif target_schema: sql = index_utils.get_reverse_engineered_sql( - self.conn, schema=self.schema, + self.conn, schema=target_schema, table=self.table, did=did, tid=tid, idx=idx, datlastsysoid=self.datlastsysoid, template_path=None, with_header=False) @@ -1070,6 +1071,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare): source = kwargs.get('source') target = kwargs.get('target') comp_status = kwargs.get('comp_status') + tgt_schema = kwargs.get('target_schema', None) diff = '' if comp_status == 'source_only': @@ -1078,7 +1080,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare): scid=src_params['scid'], tid=src_params['tid'], idx=source['oid'], - create_mode=True) + target_schema=tgt_schema) elif comp_status == 'target_only': diff = self.delete(gid=1, sid=tgt_params['sid'], did=tgt_params['did'], scid=tgt_params['scid'], @@ -1104,7 +1106,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare): scid=src_params['scid'], tid=src_params['tid'], idx=source['oid'], - create_mode=True, + target_schema=tgt_schema, drop_req=True) else: diff = self.get_sql_from_index_diff(sid=tgt_params['sid'], diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py index a82f9aa69..29d47bc17 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py @@ -614,8 +614,8 @@ class RowSecurityView(PGChildNodeView): oid = kwargs.get('plid') data = kwargs.get('data', None) drop_req = kwargs.get('drop_req', False) + target_schema = kwargs.get('target_schema', None) - sql = '' if data: data['schema'] = self.schema data['table'] = self.table @@ -626,8 +626,12 @@ class RowSecurityView(PGChildNodeView): sql = sql.strip('\n').strip(' ') else: + schema = self.schema + if target_schema: + schema = target_schema + sql = row_security_policies_utils.get_reverse_engineered_sql( - self.conn, schema=self.schema, table=self.table, scid=scid, + self.conn, schema=schema, table=self.table, scid=scid, plid=oid, datlastsysoid=self.datlastsysoid, with_header=False) drop_sql = '' @@ -688,6 +692,7 @@ class RowSecurityView(PGChildNodeView): 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 = '' @@ -697,7 +702,8 @@ class RowSecurityView(PGChildNodeView): did=src_params['did'], scid=src_params['scid'], tid=src_params['tid'], - plid=source['oid']) + plid=source['oid'], + target_schema=target_schema) elif comp_status == 'target_only': diff = self.delete(gid=1, sid=tgt_params['sid'], @@ -724,7 +730,8 @@ class RowSecurityView(PGChildNodeView): did=src_params['did'], scid=src_params['scid'], tid=src_params['tid'], - plid=source['oid']) + plid=source['oid'], + target_schema=target_schema) return delete_sql + diff diff = self.get_sql_from_diff(gid=tgt_params['gid'], diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py index 1d04b57f6..d5b49d012 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/rules/__init__.py @@ -529,6 +529,7 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, @@ -551,12 +552,28 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare): data=data, o_data=old_data ) else: + RuleView._check_schema_diff(target_schema, res_data) sql = render_template("/".join( [self.template_path, self._CREATE_SQL]), data=res_data, display_comments=True) return sql + @ staticmethod + def _check_schema_diff(target_schema, res_data): + """ + Check for schema diff, if yes then replace source schema with target + schema. + diff_schema: schema diff schema + res_data: response from properties sql. + """ + if target_schema and 'statements' in res_data: + # Replace the source schema with the target schema + res_data['statements'] = \ + res_data['statements'].replace( + res_data['schema'], target_schema) + res_data['schema'] = target_schema + @check_precondition def dependents(self, gid, sid, did, scid, tid, rid): """ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/schema_diff_utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/schema_diff_utils.py index e433a151a..edeb39ef6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/schema_diff_utils.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/schema_diff_utils.py @@ -57,6 +57,10 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): source_tables = {} target_tables = {} + status, target_schema = self.get_schema(**target_params) + if not status: + return internal_server_error(errormsg=target_schema) + if 'scid' in source_params and source_params['scid'] is not None: source_tables = self.fetch_tables(**source_params) @@ -71,6 +75,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): return compare_dictionaries(view_object=self, source_params=source_params, target_params=target_params, + target_schema=target_schema, source_dict=source_tables, target_dict=target_tables, node=self.node_type, @@ -241,6 +246,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): """ source_params = kwargs.get('source_params') target_params = kwargs.get('target_params') + target_schema = kwargs.get('target_schema') source = kwargs.get('source') target = kwargs.get('target') diff_dict = kwargs.get('diff_dict') @@ -293,20 +299,21 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): added = dict1_keys - dict2_keys diff = SchemaDiffTableCompare._compare_source_only( added, module_view, source_params, target_params, - dict1, diff) + dict1, diff, target_schema) # Keys that are available in target and missing in source. removed = dict2_keys - dict1_keys diff = SchemaDiffTableCompare._compare_target_only( removed, module_view, source_params, target_params, - dict2, diff) + dict2, diff, target_schema) # Keys that are available in both source and target. other_param = { "dict1": dict1, "dict2": dict2, "source": source, - "target": target + "target": target, + "target_schema": target_schema } diff = self._compare_source_and_target( intersect_keys, module_view, source_params, @@ -316,11 +323,12 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): @staticmethod def _compare_source_only(added, module_view, source_params, target_params, - dict1, diff): + dict1, diff, target_schema): for item in added: source_ddl = module_view.ddl_compare( source_params=source_params, target_params=target_params, + target_schema=target_schema, source=dict1[item], target=None, comp_status='source_only' @@ -331,11 +339,12 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): @staticmethod def _compare_target_only(removed, module_view, source_params, - target_params, dict2, diff): + target_params, dict2, diff, target_schema): for item in removed: target_ddl = module_view.ddl_compare( source_params=source_params, target_params=target_params, + target_schema=target_schema, source=None, target=dict2[item], comp_status='target_only' @@ -351,6 +360,8 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): dict2 = kwargs['dict2'] source = kwargs['source'] target = kwargs['target'] + target_schema = kwargs['target_schema'] + for key in intersect_keys: # Recursively Compare the two dictionary if not are_dictionaries_identical( @@ -358,6 +369,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare): diff_ddl = module_view.ddl_compare( source_params=source_params, target_params=target_params, + target_schema=target_schema, source=dict1[key], target=dict2[key], comp_status='different', diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py index f19365521..1b895f4d9 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/__init__.py @@ -866,6 +866,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: SQL, name = trigger_utils.get_sql( @@ -884,6 +885,9 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare): only_sql=True) else: schema = self.schema + if target_schema: + schema = target_schema + SQL = trigger_utils.get_reverse_engineered_sql( self.conn, schema=schema, table=self.table, tid=tid, trid=oid, datlastsysoid=self.datlastsysoid, @@ -1052,6 +1056,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare): 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 = '' @@ -1061,7 +1066,8 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare): did=src_params['did'], scid=src_params['scid'], tid=src_params['tid'], - oid=source['oid']) + oid=source['oid'], + target_schema=target_schema) elif comp_status == 'target_only': diff = self.get_sql_from_diff(gid=tgt_params['gid'], sid=tgt_params['sid'], diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py index d41c9afb9..7a8e5a267 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py @@ -1373,6 +1373,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): json_resp: True then return json response """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) SQL = render_template( "/".join([self.template_path, @@ -1390,6 +1391,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ) # Making copy of output for future use data = dict(res['rows'][0]) + if target_schema: + data['schema'] = target_schema SQL = render_template("/".join([self.template_path, self._ACL_SQL]), scid=scid, tid=tid) @@ -1521,14 +1524,20 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name = self.get_sql(gid=gid, sid=sid, scid=scid, data=data, tid=oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, tid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid, json_resp=False) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py index e54cd2a1a..ca1290128 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py @@ -1371,6 +1371,7 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare): This function will generate sql to render into the sql panel """ json_resp = kwargs.get('json_resp', True) + target_schema = kwargs.get('target_schema', None) display_comments = True if not json_resp: @@ -1390,6 +1391,10 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare): return gone(self.not_found_error_msg()) result = res['rows'][0] + if target_schema: + result['definition'] = result['definition'].replace( + result['schema'], target_schema) + result['schema'] = target_schema # sending result to formtter frmtd_reslt = self.formatter(result) @@ -1689,8 +1694,11 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare): oid = kwargs.get('oid') data = kwargs.get('data', None) drop_sql = kwargs.get('drop_sql', False) + target_schema = kwargs.get('target_schema', None) if data: + if target_schema: + data['schema'] = target_schema sql, name_or_error = self.getSQL(gid, sid, did, data, oid) if sql.find('DROP VIEW') != -1: sql = gettext(""" @@ -1703,6 +1711,9 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare): if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, vid=oid, only_sql=True) + elif target_schema: + sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, vid=oid, + target_schema=target_schema, json_resp=False) else: sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, vid=oid, json_resp=False) diff --git a/web/pgadmin/tools/schema_diff/__init__.py b/web/pgadmin/tools/schema_diff/__init__.py index 669a1dd73..887e0080e 100644 --- a/web/pgadmin/tools/schema_diff/__init__.py +++ b/web/pgadmin/tools/schema_diff/__init__.py @@ -30,6 +30,7 @@ from pgadmin.utils.constants import PREF_LABEL_DISPLAY, MIMETYPE_APP_JS,\ from sqlalchemy import or_ MODULE_NAME = 'schema_diff' +COMPARE_MSG = gettext("Comparing objects...") class SchemaDiffModule(PgAdminModule): @@ -64,7 +65,9 @@ class SchemaDiffModule(PgAdminModule): 'schema_diff.panel', 'schema_diff.servers', 'schema_diff.databases', - 'schema_diff.compare', + 'schema_diff.schemas', + 'schema_diff.compare_database', + 'schema_diff.compare_schema', 'schema_diff.poll', 'schema_diff.ddl_compare', 'schema_diff.connect_server', @@ -430,32 +433,53 @@ def databases(sid): @blueprint.route( - '/compare////' - '/', + '/schemas//', methods=["GET"], - endpoint="compare" + endpoint="schemas" ) @login_required -def compare(trans_id, source_sid, source_did, target_sid, target_did): +def schemas(sid, did): """ - This function will compare the two schemas. + This function will return the list of schemas for the specified + server id and database id. """ - # Check the transaction and connection status + res = [] + try: + schemas = get_schemas(sid, did) + if schemas is not None: + for sch in schemas: + res.append({ + "value": sch['_id'], + "label": sch['label'], + "_id": sch['_id'], + "image": sch['icon'], + }) + except Exception as e: + app.logger.exception(e) + + return make_json_response(data=res) + + +@blueprint.route( + '/compare_database////' + '/', + methods=["GET"], + endpoint="compare_database" +) +@login_required +def compare_database(trans_id, source_sid, source_did, target_sid, target_did): + """ + This function will compare the two databases. + """ + # Check the pre validation before compare status, error_msg, diff_model_obj, session_obj = \ - check_transaction_status(trans_id) - - if error_msg == ERROR_MSG_TRANS_ID_NOT_FOUND: - return make_json_response(success=0, errormsg=error_msg, status=404) - - # Server version compatibility check - status, msg = check_version_compatibility(source_sid, target_sid) - + compare_pre_validation(trans_id, source_sid, target_sid) if not status: - return make_json_response(success=0, errormsg=msg, status=428) + return error_msg comparison_result = [] - diff_model_obj.set_comparison_info(gettext("Comparing objects..."), 0) + diff_model_obj.set_comparison_info(COMPARE_MSG, 0) update_session_diff_transaction(trans_id, session_obj, diff_model_obj) @@ -552,6 +576,60 @@ def compare(trans_id, source_sid, source_did, target_sid, target_did): return make_json_response(data=comparison_result) +@blueprint.route( + '/compare_schema////' + '///', + methods=["GET"], + endpoint="compare_schema" +) +@login_required +def compare_schema(trans_id, source_sid, source_did, source_scid, + target_sid, target_did, target_scid): + """ + This function will compare the two schema. + """ + # Check the pre validation before compare + status, error_msg, diff_model_obj, session_obj = \ + compare_pre_validation(trans_id, source_sid, target_sid) + if not status: + return error_msg + + comparison_result = [] + + diff_model_obj.set_comparison_info(COMPARE_MSG, 0) + update_session_diff_transaction(trans_id, session_obj, + diff_model_obj) + try: + all_registered_nodes = SchemaDiffRegistry.get_registered_nodes() + node_percent = round(100 / len(all_registered_nodes)) + total_percent = 0 + + comparison_schema_result, total_percent = \ + compare_schema_objects( + trans_id=trans_id, session_obj=session_obj, + source_sid=source_sid, source_did=source_did, + source_scid=source_scid, target_sid=target_sid, + target_did=target_did, target_scid=target_scid, + schema_name=gettext('Schema Objects'), + diff_model_obj=diff_model_obj, + total_percent=total_percent, + node_percent=node_percent) + + comparison_result = \ + comparison_result + comparison_schema_result + + msg = gettext("Successfully compare the specified schemas.") + total_percent = 100 + diff_model_obj.set_comparison_info(msg, total_percent) + # Update the message and total percentage done in session object + update_session_diff_transaction(trans_id, session_obj, diff_model_obj) + + except Exception as e: + app.logger.exception(e) + + return make_json_response(data=comparison_result) + + @blueprint.route( '/poll/', methods=["GET"], endpoint="poll" ) @@ -573,7 +651,7 @@ def poll(trans_id): msg, diff_percentage = diff_model_obj.get_comparison_info() if diff_percentage == 100: - diff_model_obj.set_comparison_info(gettext("Comparing objects..."), 0) + diff_model_obj.set_comparison_info(COMPARE_MSG, 0) update_session_diff_transaction(trans_id, session_obj, diff_model_obj) @@ -755,9 +833,13 @@ def compare_schema_objects(**kwargs): for node_name, node_view in all_registered_nodes.items(): view = SchemaDiffRegistry.get_node_view(node_name) if hasattr(view, 'compare'): - msg = gettext('Comparing {0} of schema \'{1}\''). \ - format(gettext(view.blueprint.collection_label), - gettext(schema_name)) + if schema_name == 'Schema Objects': + msg = gettext('Comparing {0} '). \ + format(gettext(view.blueprint.collection_label)) + else: + msg = gettext('Comparing {0} of schema \'{1}\''). \ + format(gettext(view.blueprint.collection_label), + gettext(schema_name)) app.logger.debug(msg) diff_model_obj.set_comparison_info(msg, total_percent) # Update the message and total percentage in session object @@ -832,3 +914,28 @@ def fetch_compare_schemas(source_sid, source_did, target_sid, target_did): 'in_both_database': in_both_database} return schema_result + + +def compare_pre_validation(trans_id, source_sid, target_sid): + """ + This function is used to validate transaction id and version compatibility + :param trans_id: + :param source_sid: + :param target_sid: + :return: + """ + + status, error_msg, diff_model_obj, session_obj = \ + check_transaction_status(trans_id) + + if error_msg == ERROR_MSG_TRANS_ID_NOT_FOUND: + res = make_json_response(success=0, errormsg=error_msg, status=404) + return False, res, None, None + + # Server version compatibility check + status, msg = check_version_compatibility(source_sid, target_sid) + if not status: + res = make_json_response(success=0, errormsg=msg, status=428) + return False, res, None, None + + return True, '', diff_model_obj, session_obj diff --git a/web/pgadmin/tools/schema_diff/compare.py b/web/pgadmin/tools/schema_diff/compare.py index 99472ad5d..243ee9d7b 100644 --- a/web/pgadmin/tools/schema_diff/compare.py +++ b/web/pgadmin/tools/schema_diff/compare.py @@ -12,12 +12,13 @@ from flask import render_template from pgadmin.utils.driver import get_driver from config import PG_DEFAULT_DRIVER +from pgadmin.utils.ajax import internal_server_error from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries class SchemaDiffObjectCompare: - keys_to_ignore = ['oid', 'oid-2', 'is_sys_obj'] + keys_to_ignore = ['oid', 'oid-2', 'is_sys_obj', 'schema'] @staticmethod def get_schema(sid, did, scid): @@ -62,6 +63,12 @@ class SchemaDiffObjectCompare: source = {} target = {} + 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 group_name == 'Database Objects': source = self.fetch_objects_to_compare(**source_params) target = self.fetch_objects_to_compare(**target_params) @@ -83,6 +90,7 @@ class SchemaDiffObjectCompare: return compare_dictionaries(view_object=self, source_params=source_params, target_params=target_params, + target_schema=target_schema, source_dict=source, target_dict=target, node=self.node_type, diff --git a/web/pgadmin/tools/schema_diff/directory_compare.py b/web/pgadmin/tools/schema_diff/directory_compare.py index 390196703..e16581b3b 100644 --- a/web/pgadmin/tools/schema_diff/directory_compare.py +++ b/web/pgadmin/tools/schema_diff/directory_compare.py @@ -36,6 +36,7 @@ def _get_source_list(**kwargs): node_label = kwargs.get('node_label') group_name = kwargs.get('group_name') source_schema_name = kwargs.get('source_schema_name') + target_schema = kwargs.get('target_schema') global count source_only = [] @@ -50,6 +51,7 @@ def _get_source_list(**kwargs): temp_src_params['json_resp'] = False source_ddl = \ view_object.get_sql_from_table_diff(**temp_src_params) + temp_src_params.update({'target_schema': target_schema}) diff_ddl = view_object.get_sql_from_table_diff(**temp_src_params) source_dependencies = \ view_object.get_table_submodules_dependencies( @@ -65,6 +67,7 @@ def _get_source_list(**kwargs): temp_src_params['fsid'] = source_dict[item]['fsid'] source_ddl = view_object.get_sql_from_diff(**temp_src_params) + temp_src_params.update({'target_schema': target_schema}) diff_ddl = view_object.get_sql_from_diff(**temp_src_params) source_dependencies = view_object.get_dependencies( view_object.conn, source_object_id, where=None, @@ -223,6 +226,7 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict, source_params = kwargs['source_params'] target_params = kwargs['target_params'] group_name = kwargs['group_name'] + target_schema = kwargs.get('target_schema') for key in intersect_keys: source_object_id, target_object_id = \ get_source_target_oid(source_dict, target_dict, key) @@ -281,7 +285,8 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict, diff_ddl = view_object.get_sql_from_submodule_diff( source_params=temp_src_params, target_params=temp_tgt_params, - source=dict1[key], target=dict2[key], diff_dict=diff_dict) + source=dict1[key], target=dict2[key], diff_dict=diff_dict, + target_schema=target_schema) else: temp_src_params = copy.deepcopy(source_params) temp_tgt_params = copy.deepcopy(target_params) @@ -303,7 +308,7 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict, show_system_objects=None, is_schema_diff=True) target_ddl = view_object.get_sql_from_diff(**temp_tgt_params) temp_tgt_params.update( - {'data': diff_dict}) + {'data': diff_dict, 'target_schema': target_schema}) diff_ddl = view_object.get_sql_from_diff(**temp_tgt_params) different.append({ @@ -336,6 +341,7 @@ def compare_dictionaries(**kwargs): view_object = kwargs.get('view_object') source_params = kwargs.get('source_params') target_params = kwargs.get('target_params') + target_schema = kwargs.get('target_schema') group_name = kwargs.get('group_name') source_dict = kwargs.get('source_dict') target_dict = kwargs.get('target_dict') @@ -364,7 +370,8 @@ def compare_dictionaries(**kwargs): view_object=view_object, node_label=node_label, group_name=group_name, - source_schema_name=source_schema_name) + source_schema_name=source_schema_name, + target_schema=target_schema) target_only = [] # Keys that are available in target and missing in source. @@ -389,7 +396,8 @@ def compare_dictionaries(**kwargs): "ignore_keys": ignore_keys, "source_params": source_params, "target_params": target_params, - "group_name": group_name + "group_name": group_name, + "target_schema": target_schema } identical, different = _get_identical_and_different_list( @@ -507,7 +515,8 @@ def are_dictionaries_identical(source_dict, target_dict, ignore_keys): current_app.logger.debug( "Schema Diff: Object name: '{0}', Source Value: '{1}', " "Target Value: '{2}', Key: '{3}'".format( - source_dict['name'], source_value, target_value, key)) + source_dict['name'] if 'name' in source_dict else '', + source_value, target_value, key)) return False return True diff --git a/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js b/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js index 1b095db98..8a9bc9001 100644 --- a/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js +++ b/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js @@ -105,7 +105,7 @@ let SchemaDiffSelect2Control = controlsClassName: 'pgadmin-controls pg-el-sm-11 pg-el-12', }), className: function() { - return 'pgadmin-controls pg-el-sm-6'; + return 'pgadmin-controls pg-el-sm-4'; }, events: { 'focus select': 'clearInvalid', diff --git a/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js b/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js index fad6485c9..0a5411734 100644 --- a/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js +++ b/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js @@ -43,8 +43,10 @@ export default class SchemaDiffUI { this.model = new Backbone.Model({ source_sid: undefined, source_did: undefined, + source_scid: undefined, target_sid: undefined, target_did: undefined, + target_scid: undefined, source_ddl: undefined, target_ddl: undefined, diff_ddl: undefined, @@ -162,7 +164,12 @@ export default class SchemaDiffUI { url_params[key] = parseInt(val, 10); }); - var baseUrl = url_for('schema_diff.compare', url_params); + var baseUrl = url_for('schema_diff.compare_database', url_params); + // If compare two schema then change the base url + if (url_params['source_scid'] != '' && !_.isUndefined(url_params['source_scid']) && + url_params['target_scid'] != '' && !_.isUndefined(url_params['target_scid'])) { + baseUrl = url_for('schema_diff.compare_schema', url_params); + } self.model.set({ 'source_ddl': undefined, @@ -305,7 +312,7 @@ export default class SchemaDiffUI { // Format Schema object title with appropriate icon var formatColumnTitle = function (row, cell, value, columnDef, dataContext) { let icon = 'icon-' + dataContext.type; - return '' + value + ''; + return '' + value + ''; }; // Grid Columns @@ -648,6 +655,30 @@ export default class SchemaDiffUI { connect: function() { self.connect_database(this.model.get('source_sid'), arguments[0], arguments[1]); }, + }, { + name: 'source_scid', + control: SchemaDiffSelect2Control, + group: 'source', + deps: ['source_sid', 'source_did'], + url: function() { + if (this.get('source_sid') && this.get('source_did')) + return url_for('schema_diff.schemas', {'sid': this.get('source_sid'), 'did': this.get('source_did')}); + return false; + }, + select2: { + allowClear: true, + placeholder: gettext('Select schema...'), + }, + disabled: function(m) { + if (!_.isUndefined(m.get('source_did')) && !_.isNull(m.get('source_did')) + && m.get('source_did') !== '') { + return false; + } + setTimeout(function() { + m.set('source_scid', undefined); + }, 10); + return true; + }, }, { name: 'target_sid', label: false, control: SchemaDiffSelect2Control, @@ -708,6 +739,30 @@ export default class SchemaDiffUI { connect: function() { self.connect_database(this.model.get('target_sid'), arguments[0], arguments[1]); }, + }, { + name: 'target_scid', + control: SchemaDiffSelect2Control, + group: 'target', + deps: ['target_sid', 'target_did'], + url: function() { + if (this.get('target_sid') && this.get('target_did')) + return url_for('schema_diff.schemas', {'sid': this.get('target_sid'), 'did': this.get('target_did')}); + return false; + }, + select2: { + allowClear: true, + placeholder: gettext('Select schema...'), + }, + disabled: function(m) { + if (!_.isUndefined(m.get('target_did')) && !_.isNull(m.get('target_did')) + && m.get('target_did') !== '') { + return false; + } + setTimeout(function() { + m.set('target_scid', undefined); + }, 10); + return true; + }, }], }); @@ -739,7 +794,9 @@ export default class SchemaDiffUI { footer_panel.$container.find('#schema-diff-ddl-comp').append(self.footer.render().$el); header_panel.$container.find('#schema-diff-grid').append(`
-
` + gettext('Select the server and database for the source and target and click Compare to compare them.') + '
'); +
` + gettext('Database Compare: Select the server and database for the source and target and Click Compare.') + + gettext('
Schema Compare: Select the server, database and schema for the source and target and Click Compare.') + + gettext('
Note: The dependencies will not be resolved in the Schema comparison.') + '
'); self.grid_width = $('#schema-diff-grid').width(); self.grid_height = this.panel_obj.height(); diff --git a/web/pgadmin/tools/schema_diff/tests/test_schema_diff_comp.py b/web/pgadmin/tools/schema_diff/tests/test_schema_diff_comp.py index 3a0c7ea6f..c8320b815 100644 --- a/web/pgadmin/tools/schema_diff/tests/test_schema_diff_comp.py +++ b/web/pgadmin/tools/schema_diff/tests/test_schema_diff_comp.py @@ -25,7 +25,7 @@ class SchemaDiffTestCase(BaseTestGenerator): scenarios = [ # Fetching default URL for database node. ('Schema diff comparison', dict( - url='schema_diff/compare/{0}/{1}/{2}/{3}/{4}')) + url='schema_diff/compare_database/{0}/{1}/{2}/{3}/{4}')) ] def setUp(self):