mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-07-29 15:53:54 -05:00
Fixed an issue where Triggers, Rules, Indexes were absent from the Schema Diff when comparing views. #7271
Fixed an issue in Schema Diff where Columns with sequences get altered unnecessarily. #4413
This commit is contained in:
@@ -15,9 +15,9 @@ as a background process for the DBMS_SCHEDULER and DBMS_JOB packages.
|
|||||||
|
|
||||||
The EDB Job Scheduler has a scheduler process that starts when the database cluster starts.
|
The EDB Job Scheduler has a scheduler process that starts when the database cluster starts.
|
||||||
To start the scheduler process, load the EDB Job Scheduler extension using the **shared_preload_libraries**
|
To start the scheduler process, load the EDB Job Scheduler extension using the **shared_preload_libraries**
|
||||||
parameter. After you load the extension, create the extension using the CREATE EXTENSION command.
|
parameter. After you load the extension, create the extension **'edb_job_scheduler'** and **'dbms_scheduler'** using
|
||||||
The database in which you're creating the extension must be listed in the **edb_job_scheduler.database_list**
|
the CREATE EXTENSION command. The database in which you're creating the extension must be listed in the
|
||||||
parameter.
|
**edb_job_scheduler.database_list** parameter.
|
||||||
|
|
||||||
Instructions for configuring the EDB Job Scheduler can be found in the
|
Instructions for configuring the EDB Job Scheduler can be found in the
|
||||||
`Configuring EDB Job Scheduler <https://www.enterprisedb.com/docs/pg_extensions/edb_job_scheduler/configuring/>`_.
|
`Configuring EDB Job Scheduler <https://www.enterprisedb.com/docs/pg_extensions/edb_job_scheduler/configuring/>`_.
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ Bundled PostgreSQL Utilities
|
|||||||
New features
|
New features
|
||||||
************
|
************
|
||||||
|
|
||||||
|
| `Issue #5611 <https://github.com/pgadmin-org/pgadmin4/issues/5611>`_ - Added support for provider, deterministic, version and RULES parameter while creating collation.
|
||||||
| `Issue #7098 <https://github.com/pgadmin-org/pgadmin4/issues/7098>`_ - Added support for EDB Job Scheduler.
|
| `Issue #7098 <https://github.com/pgadmin-org/pgadmin4/issues/7098>`_ - Added support for EDB Job Scheduler.
|
||||||
| `Issue #7163 <https://github.com/pgadmin-org/pgadmin4/issues/7163>`_ - Added support to exclude multiple tables while taking backup.
|
|
||||||
| `Issue #7221 <https://github.com/pgadmin-org/pgadmin4/issues/7221>`_ - Added support for UNIX socket in entrypoint.sh for Docker implementation.
|
| `Issue #7221 <https://github.com/pgadmin-org/pgadmin4/issues/7221>`_ - Added support for UNIX socket in entrypoint.sh for Docker implementation.
|
||||||
|
|
||||||
Housekeeping
|
Housekeeping
|
||||||
@@ -31,12 +31,16 @@ Housekeeping
|
|||||||
Bug fixes
|
Bug fixes
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
| `Issue #4413 <https://github.com/pgadmin-org/pgadmin4/issues/4413>`_ - Fixed an issue in Schema Diff where Columns with sequences get altered unnecessarily.
|
||||||
| `Issue #7116 <https://github.com/pgadmin-org/pgadmin4/issues/7116>`_ - Bug fixes and improvements in pgAdmin CLI.
|
| `Issue #7116 <https://github.com/pgadmin-org/pgadmin4/issues/7116>`_ - Bug fixes and improvements in pgAdmin CLI.
|
||||||
| `Issue #7165 <https://github.com/pgadmin-org/pgadmin4/issues/7165>`_ - Fixed schema diff wrong query generation for table, foreign table and sequence.
|
| `Issue #7165 <https://github.com/pgadmin-org/pgadmin4/issues/7165>`_ - Fixed schema diff wrong query generation for table, foreign table and sequence.
|
||||||
| `Issue #7229 <https://github.com/pgadmin-org/pgadmin4/issues/7229>`_ - Fix an issue in table dialog where changing column name was not syncing table constraints appropriately.
|
| `Issue #7229 <https://github.com/pgadmin-org/pgadmin4/issues/7229>`_ - Fix an issue in table dialog where changing column name was not syncing table constraints appropriately.
|
||||||
|
| `Issue #7255 <https://github.com/pgadmin-org/pgadmin4/issues/7255>`_ - Fixed an issue where taking backup of a shared server was using server owner's user name.
|
||||||
| `Issue #7262 <https://github.com/pgadmin-org/pgadmin4/issues/7262>`_ - Fix an issue in editor where replace option in query tool edit menu is not working on non-Mac OS.
|
| `Issue #7262 <https://github.com/pgadmin-org/pgadmin4/issues/7262>`_ - Fix an issue in editor where replace option in query tool edit menu is not working on non-Mac OS.
|
||||||
| `Issue #7268 <https://github.com/pgadmin-org/pgadmin4/issues/7268>`_ - Fix an issue in editor where Format SQL shortcut and multiline selection are not working.
|
| `Issue #7268 <https://github.com/pgadmin-org/pgadmin4/issues/7268>`_ - Fix an issue in editor where Format SQL shortcut and multiline selection are not working.
|
||||||
| `Issue #7269 <https://github.com/pgadmin-org/pgadmin4/issues/7269>`_ - Fix an issue in editor where "Use Spaces?" Preference of Editor is not working.
|
| `Issue #7269 <https://github.com/pgadmin-org/pgadmin4/issues/7269>`_ - Fix an issue in editor where "Use Spaces?" Preference of Editor is not working.
|
||||||
|
| `Issue #7271 <https://github.com/pgadmin-org/pgadmin4/issues/7271>`_ - Fixed an issue where Triggers, Rules, Indexes were absent from the Schema Diff when comparing views.
|
||||||
| `Issue #7277 <https://github.com/pgadmin-org/pgadmin4/issues/7277>`_ - Fix an issue in query tool where toggle case of selected text loses selection.
|
| `Issue #7277 <https://github.com/pgadmin-org/pgadmin4/issues/7277>`_ - Fix an issue in query tool where toggle case of selected text loses selection.
|
||||||
| `Issue #7299 <https://github.com/pgadmin-org/pgadmin4/issues/7299>`_ - Fix query tool autocomplete results when cursor is in between the SQL query.
|
| `Issue #7299 <https://github.com/pgadmin-org/pgadmin4/issues/7299>`_ - Fix query tool autocomplete results when cursor is in between the SQL query.
|
||||||
| `Issue #7305 <https://github.com/pgadmin-org/pgadmin4/issues/7305>`_ - Fix an issue in query tool where custom keyboard shortcuts are not working for some.
|
| `Issue #7305 <https://github.com/pgadmin-org/pgadmin4/issues/7305>`_ - Fix an issue in query tool where custom keyboard shortcuts are not working for some.
|
||||||
|
| `Issue #7308 <https://github.com/pgadmin-org/pgadmin4/issues/7308>`_ - Fixed issue related to email authentication of Two-factor authentication.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from .utils import BaseTableView
|
|||||||
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
||||||
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_table_utils import SchemaDiffTableCompare
|
||||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
||||||
columns import utils as column_utils
|
columns import utils as column_utils
|
||||||
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
|
|||||||
'relacl_str', 'setting']
|
'relacl_str', 'setting']
|
||||||
|
|
||||||
column_keys_to_ignore = ['atttypid', 'edit_types', 'elemoid', 'seqrelid',
|
column_keys_to_ignore = ['atttypid', 'edit_types', 'elemoid', 'seqrelid',
|
||||||
'indkey']
|
'indkey', 'seqtypid', 'defval']
|
||||||
|
|
||||||
constraint_keys_to_ignore = ['relname', 'nspname', 'parent_tbl',
|
constraint_keys_to_ignore = ['relname', 'nspname', 'parent_tbl',
|
||||||
'attrelid', 'adrelid', 'fknsp', 'confrelid',
|
'attrelid', 'adrelid', 'fknsp', 'confrelid',
|
||||||
+1
@@ -14,6 +14,7 @@ WHERE
|
|||||||
rw.oid = {{ rid }}
|
rw.oid = {{ rid }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if schema_diff %}
|
{% if schema_diff %}
|
||||||
|
AND rw.rulename != '_RETURN'
|
||||||
AND CASE WHEN (SELECT COUNT(*) FROM pg_catalog.pg_depend
|
AND CASE WHEN (SELECT COUNT(*) FROM pg_catalog.pg_depend
|
||||||
WHERE objid = rw.oid AND deptype = 'e') > 0 THEN FALSE ELSE TRUE END
|
WHERE objid = rw.oid AND deptype = 'e') > 0 THEN FALSE ELSE TRUE END
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -546,7 +546,6 @@ class BaseTableView(PGChildNodeView, BasePartitionTable, VacuumSettings):
|
|||||||
BaseTableView._get_sub_module_data_for_compare(
|
BaseTableView._get_sub_module_data_for_compare(
|
||||||
self, sid, did, scid, data, row)
|
self, sid, did, scid, data, row)
|
||||||
res[row['name']] = data
|
res[row['name']] = data
|
||||||
res[row['name']] = data
|
|
||||||
|
|
||||||
return True, res
|
return True, res
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
|
|||||||
make_response as ajax_response, gone
|
make_response as ajax_response, gone
|
||||||
from pgadmin.utils.driver import get_driver
|
from pgadmin.utils.driver import get_driver
|
||||||
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 .schema_diff_view_utils import SchemaDiffViewCompare
|
||||||
from pgadmin.utils import html, does_utility_exist, get_server
|
from pgadmin.utils import html, does_utility_exist, get_server
|
||||||
from pgadmin.model import Server
|
from pgadmin.model import Server
|
||||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||||
@@ -260,6 +260,24 @@ def check_precondition(f):
|
|||||||
self.column_template_path = 'columns/sql/#{0}#'.format(
|
self.column_template_path = 'columns/sql/#{0}#'.format(
|
||||||
self.manager.version)
|
self.manager.version)
|
||||||
|
|
||||||
|
# Template for trigger node
|
||||||
|
self.trigger_template_path = 'triggers/sql/{0}/#{1}#'.format(
|
||||||
|
self.manager.server_type, self.manager.version)
|
||||||
|
|
||||||
|
# Template for compound trigger node
|
||||||
|
self.compound_trigger_template_path = (
|
||||||
|
'compound_triggers/sql/{0}/#{1}#'.format(
|
||||||
|
self.manager.server_type, self.manager.version))
|
||||||
|
|
||||||
|
# Template for rules node
|
||||||
|
self.rules_template_path = 'rules/sql'
|
||||||
|
|
||||||
|
# Submodule list for schema diff
|
||||||
|
self.view_sub_modules = ['rule', 'trigger']
|
||||||
|
if (self.manager.server_type == 'ppas' and
|
||||||
|
self.manager.version >= 120000):
|
||||||
|
self.view_sub_modules.append('compound_trigger')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.allowed_acls = render_template(
|
self.allowed_acls = render_template(
|
||||||
"/".join([self.template_path, self._ALLOWED_PRIVS_JSON])
|
"/".join([self.template_path, self._ALLOWED_PRIVS_JSON])
|
||||||
@@ -273,7 +291,7 @@ def check_precondition(f):
|
|||||||
return wrap
|
return wrap
|
||||||
|
|
||||||
|
|
||||||
class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffViewCompare):
|
||||||
"""
|
"""
|
||||||
This class is responsible for generating routes for view node.
|
This class is responsible for generating routes for view node.
|
||||||
|
|
||||||
@@ -374,8 +392,6 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
{'get': 'get_toast_table_vacuum'}]
|
{'get': 'get_toast_table_vacuum'}]
|
||||||
})
|
})
|
||||||
|
|
||||||
keys_to_ignore = ['oid', 'schema', 'xmin', 'oid-2', 'setting']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize the variables used by methods of ViewNode.
|
Initialize the variables used by methods of ViewNode.
|
||||||
@@ -510,7 +526,7 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
# sending result to formtter
|
# sending result to formtter
|
||||||
frmtd_reslt = self.formatter(result)
|
frmtd_reslt = self.formatter(result)
|
||||||
|
|
||||||
# merging formated result with main result again
|
# merging formatted result with main result again
|
||||||
result.update(frmtd_reslt)
|
result.update(frmtd_reslt)
|
||||||
|
|
||||||
return True, result
|
return True, result
|
||||||
@@ -1140,7 +1156,8 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
[self.ct_trigger_temp_path,
|
[self.ct_trigger_temp_path,
|
||||||
'sql/{0}/#{1}#/create.sql'.format(
|
'sql/{0}/#{1}#/create.sql'.format(
|
||||||
self.manager.server_type, self.manager.version)]),
|
self.manager.server_type, self.manager.version)]),
|
||||||
data=res_rows, display_comments=display_comments)
|
data=res_rows, display_comments=display_comments,
|
||||||
|
conn=self.conn)
|
||||||
sql_data += '\n'
|
sql_data += '\n'
|
||||||
sql_data += SQL
|
sql_data += SQL
|
||||||
|
|
||||||
@@ -1264,7 +1281,8 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
[self.trigger_temp_path,
|
[self.trigger_temp_path,
|
||||||
'sql/{0}/#{1}#/create.sql'.format(
|
'sql/{0}/#{1}#/create.sql'.format(
|
||||||
self.manager.server_type, self.manager.version)]),
|
self.manager.server_type, self.manager.version)]),
|
||||||
data=res_rows, display_comments=display_comments)
|
data=res_rows, display_comments=display_comments,
|
||||||
|
conn=self.conn)
|
||||||
sql_data += '\n'
|
sql_data += '\n'
|
||||||
sql_data += SQL
|
sql_data += SQL
|
||||||
|
|
||||||
@@ -1310,7 +1328,8 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.index_temp_path,
|
[self.index_temp_path,
|
||||||
'sql/#{0}#/create.sql'.format(self.manager.version)]),
|
'sql/#{0}#/create.sql'.format(self.manager.version)]),
|
||||||
data=data, display_comments=display_comments)
|
data=data, display_comments=display_comments,
|
||||||
|
conn=self.conn)
|
||||||
sql_data += '\n'
|
sql_data += '\n'
|
||||||
sql_data += SQL
|
sql_data += SQL
|
||||||
return sql_data
|
return sql_data
|
||||||
@@ -1680,6 +1699,19 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
|
|
||||||
return ajax_response(response=sql)
|
return ajax_response(response=sql)
|
||||||
|
|
||||||
|
def _get_sub_module_data_for_compare(self, sid, did, scid, data, vid):
|
||||||
|
# Get sub module data of a specified view for object
|
||||||
|
# comparison
|
||||||
|
for module in self.view_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=vid,
|
||||||
|
oid=None)
|
||||||
|
data[module] = sub_data
|
||||||
|
|
||||||
@check_precondition
|
@check_precondition
|
||||||
def fetch_objects_to_compare(self, sid, did, scid, oid=None):
|
def fetch_objects_to_compare(self, sid, did, scid, oid=None):
|
||||||
"""
|
"""
|
||||||
@@ -1707,6 +1739,9 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
for row in views['rows']:
|
for row in views['rows']:
|
||||||
status, data = self._fetch_properties(scid, row['oid'])
|
status, data = self._fetch_properties(scid, row['oid'])
|
||||||
if status:
|
if status:
|
||||||
|
# Fetch the data of sub module
|
||||||
|
self._get_sub_module_data_for_compare(
|
||||||
|
sid, did, scid, data, row['oid'])
|
||||||
res[row['name']] = data
|
res[row['name']] = data
|
||||||
else:
|
else:
|
||||||
status, data = self._fetch_properties(scid, oid)
|
status, data = self._fetch_properties(scid, oid)
|
||||||
@@ -1717,7 +1752,7 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_sql_from_diff(self, **kwargs):
|
def get_sql_from_view_diff(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
This function is used to get the DDL/DML statements.
|
This function is used to get the DDL/DML statements.
|
||||||
:param kwargs
|
:param kwargs
|
||||||
@@ -1727,15 +1762,15 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
sid = kwargs.get('sid')
|
sid = kwargs.get('sid')
|
||||||
did = kwargs.get('did')
|
did = kwargs.get('did')
|
||||||
scid = kwargs.get('scid')
|
scid = kwargs.get('scid')
|
||||||
oid = kwargs.get('oid')
|
oid = kwargs.get('tid')
|
||||||
data = kwargs.get('data', None)
|
diff_data = kwargs.get('diff_data', None)
|
||||||
drop_sql = kwargs.get('drop_sql', False)
|
drop_sql = kwargs.get('drop_sql', False)
|
||||||
target_schema = kwargs.get('target_schema', None)
|
target_schema = kwargs.get('target_schema', None)
|
||||||
|
|
||||||
if data:
|
if diff_data:
|
||||||
if target_schema:
|
if target_schema:
|
||||||
data['schema'] = target_schema
|
diff_data['schema'] = target_schema
|
||||||
sql, _ = self.getSQL(gid, sid, did, data, oid)
|
sql, _ = self.getSQL(gid, sid, did, diff_data, oid)
|
||||||
if sql.find('DROP VIEW') != -1:
|
if sql.find('DROP VIEW') != -1:
|
||||||
sql = gettext("""
|
sql = gettext("""
|
||||||
-- Changing the columns in a view requires dropping and re-creating the view.
|
-- Changing the columns in a view requires dropping and re-creating the view.
|
||||||
@@ -1755,6 +1790,60 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
|
|||||||
json_resp=False)
|
json_resp=False)
|
||||||
return sql
|
return sql
|
||||||
|
|
||||||
|
def get_submodule_template_path(self, module_name):
|
||||||
|
"""
|
||||||
|
This function is used to get the template path based on module name.
|
||||||
|
:param module_name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
template_path = None
|
||||||
|
if module_name == 'trigger':
|
||||||
|
template_path = self.trigger_template_path
|
||||||
|
elif module_name == 'rule':
|
||||||
|
template_path = self.rules_template_path
|
||||||
|
elif module_name == 'compound_trigger':
|
||||||
|
template_path = self.compound_trigger_template_path
|
||||||
|
|
||||||
|
return template_path
|
||||||
|
|
||||||
|
def get_view_submodules_dependencies(self, **kwargs):
|
||||||
|
"""
|
||||||
|
This function is used to get the dependencies of view and it's
|
||||||
|
submodules.
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
vid = kwargs['tid']
|
||||||
|
view_dependencies = []
|
||||||
|
view_deps = self.get_dependencies(self.conn, vid)
|
||||||
|
if len(view_deps) > 0:
|
||||||
|
view_dependencies.extend(view_deps)
|
||||||
|
|
||||||
|
# Iterate all the submodules of the table and fetch the dependencies.
|
||||||
|
for module in self.view_sub_modules:
|
||||||
|
module_view = SchemaDiffRegistry.get_node_view(module)
|
||||||
|
template_path = self.get_submodule_template_path(module)
|
||||||
|
|
||||||
|
SQL = render_template("/".join([template_path,
|
||||||
|
'nodes.sql']), tid=vid)
|
||||||
|
status, rset = self.conn.execute_2darray(SQL)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rset)
|
||||||
|
|
||||||
|
for row in rset['rows']:
|
||||||
|
result = module_view.get_dependencies(
|
||||||
|
self.conn, row['oid'], where=None,
|
||||||
|
show_system_objects=None, is_schema_diff=True)
|
||||||
|
if len(result) > 0:
|
||||||
|
view_dependencies.extend(result)
|
||||||
|
|
||||||
|
# Remove the same table from the dependency list
|
||||||
|
for item in view_dependencies:
|
||||||
|
if 'oid' in item and item['oid'] == vid:
|
||||||
|
view_dependencies.remove(item)
|
||||||
|
|
||||||
|
return view_dependencies
|
||||||
|
|
||||||
|
|
||||||
# Override the operations for materialized view
|
# Override the operations for materialized view
|
||||||
mview_operations = {
|
mview_operations = {
|
||||||
|
|||||||
+238
@@ -0,0 +1,238 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
""" Implements Utility class for View."""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from pgadmin.utils.ajax import internal_server_error
|
||||||
|
from pgadmin.tools.schema_diff.directory_compare import compare_dictionaries,\
|
||||||
|
are_dictionaries_identical
|
||||||
|
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
|
||||||
|
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaDiffViewCompare(SchemaDiffObjectCompare):
|
||||||
|
view_keys_to_ignore = ['oid', 'schema', 'xmin', 'oid-2', 'setting']
|
||||||
|
|
||||||
|
trigger_keys_to_ignore = ['xmin', 'tgrelid', 'tgfoid', 'tfunction',
|
||||||
|
'tgqual', 'tgconstraint', 'nspname']
|
||||||
|
|
||||||
|
keys_to_ignore = view_keys_to_ignore + trigger_keys_to_ignore
|
||||||
|
|
||||||
|
def compare(self, **kwargs):
|
||||||
|
"""
|
||||||
|
This function is used to compare all the table objects
|
||||||
|
from two different schemas.
|
||||||
|
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
source_params = {'sid': kwargs.get('source_sid'),
|
||||||
|
'did': kwargs.get('source_did'),
|
||||||
|
'scid': kwargs.get('source_scid')}
|
||||||
|
target_params = {'sid': kwargs.get('target_sid'),
|
||||||
|
'did': kwargs.get('target_did'),
|
||||||
|
'scid': kwargs.get('target_scid')}
|
||||||
|
ignore_owner = kwargs.get('ignore_owner')
|
||||||
|
ignore_whitespaces = kwargs.get('ignore_whitespaces')
|
||||||
|
ignore_tablespace = kwargs.get('ignore_tablespace')
|
||||||
|
ignore_grants = kwargs.get('ignore_grants')
|
||||||
|
|
||||||
|
group_name = kwargs.get('group_name')
|
||||||
|
source_schema_name = kwargs.get('source_schema_name', None)
|
||||||
|
source_views = {}
|
||||||
|
target_views = {}
|
||||||
|
|
||||||
|
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_views = self.fetch_objects_to_compare(**source_params)
|
||||||
|
|
||||||
|
if 'scid' in target_params and target_params['scid'] is not None:
|
||||||
|
target_views = self.fetch_objects_to_compare(**target_params)
|
||||||
|
|
||||||
|
# If both the dict have no items then return None.
|
||||||
|
if not (source_views or target_views) or (
|
||||||
|
len(source_views) <= 0 and len(target_views) <= 0):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return compare_dictionaries(view_object=self,
|
||||||
|
source_params=source_params,
|
||||||
|
target_params=target_params,
|
||||||
|
target_schema=target_schema,
|
||||||
|
source_dict=source_views,
|
||||||
|
target_dict=target_views,
|
||||||
|
node=self.node_type,
|
||||||
|
node_label=self.blueprint.collection_label,
|
||||||
|
group_name=group_name,
|
||||||
|
ignore_keys=self.keys_to_ignore,
|
||||||
|
source_schema_name=source_schema_name,
|
||||||
|
ignore_owner=ignore_owner,
|
||||||
|
ignore_whitespaces=ignore_whitespaces,
|
||||||
|
ignore_tablespace=ignore_tablespace,
|
||||||
|
ignore_grants=ignore_grants)
|
||||||
|
|
||||||
|
def ddl_compare(self, **kwargs):
|
||||||
|
"""
|
||||||
|
This function will compare properties of 2 views and
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
target_params = {'sid': kwargs.get('target_sid'),
|
||||||
|
'did': kwargs.get('target_did'),
|
||||||
|
'scid': kwargs.get('target_scid'),
|
||||||
|
'tid': kwargs.get('target_oid')
|
||||||
|
}
|
||||||
|
|
||||||
|
source = self.get_sql_from_view_diff(**source_params)
|
||||||
|
target = self.get_sql_from_view_diff(**target_params)
|
||||||
|
|
||||||
|
return {'source_ddl': source,
|
||||||
|
'target_ddl': target,
|
||||||
|
'diff_ddl': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_sql_from_submodule_diff(self, **kwargs):
|
||||||
|
"""
|
||||||
|
This function returns the DDL/DML statements of the
|
||||||
|
submodules of table based on the comparison status.
|
||||||
|
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
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')
|
||||||
|
ignore_whitespaces = kwargs.get('ignore_whitespaces')
|
||||||
|
diff = ''
|
||||||
|
|
||||||
|
# Get the difference DDL/DML statements for table
|
||||||
|
if isinstance(diff_dict, dict) and len(diff_dict) > 0:
|
||||||
|
target_params['diff_data'] = diff_dict
|
||||||
|
diff = self.get_sql_from_view_diff(**target_params)
|
||||||
|
|
||||||
|
ignore_sub_modules = ['column', 'index']
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
diff = SchemaDiffViewCompare._compare_source_only(
|
||||||
|
added, module_view, source_params, target_params,
|
||||||
|
dict1, diff, target_schema)
|
||||||
|
|
||||||
|
# Keys that are available in target and missing in source.
|
||||||
|
removed = dict2_keys - dict1_keys
|
||||||
|
diff = SchemaDiffViewCompare._compare_target_only(
|
||||||
|
removed, module_view, source_params, target_params,
|
||||||
|
dict2, diff, target_schema)
|
||||||
|
|
||||||
|
# Keys that are available in both source and target.
|
||||||
|
other_param = {
|
||||||
|
"dict1": dict1,
|
||||||
|
"dict2": dict2,
|
||||||
|
"source": source,
|
||||||
|
"target": target,
|
||||||
|
"target_schema": target_schema,
|
||||||
|
"ignore_whitespaces": ignore_whitespaces
|
||||||
|
}
|
||||||
|
diff = self._compare_source_and_target(
|
||||||
|
intersect_keys, module_view, source_params,
|
||||||
|
target_params, diff, **other_param)
|
||||||
|
|
||||||
|
return diff
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compare_source_only(added, module_view, source_params, target_params,
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
|
||||||
|
diff += '\n' + source_ddl
|
||||||
|
return diff
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compare_target_only(removed, module_view, source_params,
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
|
||||||
|
diff += '\n' + target_ddl
|
||||||
|
return diff
|
||||||
|
|
||||||
|
def _compare_source_and_target(self, intersect_keys, module_view,
|
||||||
|
source_params, target_params, diff,
|
||||||
|
**kwargs):
|
||||||
|
dict1 = kwargs['dict1']
|
||||||
|
dict2 = kwargs['dict2']
|
||||||
|
source = kwargs['source']
|
||||||
|
target = kwargs['target']
|
||||||
|
target_schema = kwargs['target_schema']
|
||||||
|
ignore_whitespaces = kwargs.get('ignore_whitespaces')
|
||||||
|
|
||||||
|
for key in intersect_keys:
|
||||||
|
# Recursively Compare the two dictionary
|
||||||
|
if not are_dictionaries_identical(
|
||||||
|
dict1[key], dict2[key], self.keys_to_ignore,
|
||||||
|
ignore_whitespaces):
|
||||||
|
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',
|
||||||
|
parent_source_data=source,
|
||||||
|
parent_target_data=target
|
||||||
|
)
|
||||||
|
|
||||||
|
diff += '\n' + diff_ddl
|
||||||
|
return diff
|
||||||
@@ -58,18 +58,29 @@ def _get_source_list(**kwargs):
|
|||||||
if 'oid' in source_dict[item]:
|
if 'oid' in source_dict[item]:
|
||||||
source_object_id = source_dict[item]['oid']
|
source_object_id = source_dict[item]['oid']
|
||||||
|
|
||||||
if node == 'table':
|
if node == 'table' or node == 'view':
|
||||||
temp_src_params = copy.deepcopy(source_params)
|
temp_src_params = copy.deepcopy(source_params)
|
||||||
temp_src_params['tid'] = source_object_id
|
temp_src_params['tid'] = source_object_id
|
||||||
temp_src_params['json_resp'] = False
|
temp_src_params['json_resp'] = False
|
||||||
temp_src_params['add_not_exists_clause'] = True
|
temp_src_params['add_not_exists_clause'] = True
|
||||||
source_ddl = \
|
if node == 'table':
|
||||||
view_object.get_sql_from_table_diff(**temp_src_params)
|
source_ddl = \
|
||||||
temp_src_params.update({'target_schema': target_schema})
|
view_object.get_sql_from_table_diff(**temp_src_params)
|
||||||
diff_ddl = view_object.get_sql_from_table_diff(**temp_src_params)
|
temp_src_params.update({'target_schema': target_schema})
|
||||||
source_dependencies = \
|
diff_ddl = (
|
||||||
view_object.get_table_submodules_dependencies(
|
view_object.get_sql_from_table_diff(**temp_src_params))
|
||||||
**temp_src_params)
|
source_dependencies = \
|
||||||
|
view_object.get_table_submodules_dependencies(
|
||||||
|
**temp_src_params)
|
||||||
|
elif node == 'view':
|
||||||
|
source_ddl = \
|
||||||
|
view_object.get_sql_from_view_diff(**temp_src_params)
|
||||||
|
temp_src_params.update({'target_schema': target_schema})
|
||||||
|
diff_ddl = (
|
||||||
|
view_object.get_sql_from_view_diff(**temp_src_params))
|
||||||
|
source_dependencies = \
|
||||||
|
view_object.get_view_submodules_dependencies(
|
||||||
|
**temp_src_params)
|
||||||
else:
|
else:
|
||||||
temp_src_params = copy.deepcopy(source_params)
|
temp_src_params = copy.deepcopy(source_params)
|
||||||
temp_src_params['oid'] = source_object_id
|
temp_src_params['oid'] = source_object_id
|
||||||
@@ -145,14 +156,23 @@ def _get_target_list(removed, target_dict, node, target_params, view_object,
|
|||||||
if 'oid' in target_dict[item]:
|
if 'oid' in target_dict[item]:
|
||||||
target_object_id = target_dict[item]['oid']
|
target_object_id = target_dict[item]['oid']
|
||||||
|
|
||||||
if node == 'table':
|
if node == 'table' or node == 'view':
|
||||||
temp_tgt_params = copy.deepcopy(target_params)
|
temp_tgt_params = copy.deepcopy(target_params)
|
||||||
temp_tgt_params['tid'] = target_object_id
|
temp_tgt_params['tid'] = target_object_id
|
||||||
temp_tgt_params['json_resp'] = False
|
temp_tgt_params['json_resp'] = False
|
||||||
temp_tgt_params['add_not_exists_clause'] = True
|
temp_tgt_params['add_not_exists_clause'] = True
|
||||||
target_ddl = view_object.get_sql_from_table_diff(**temp_tgt_params)
|
if node == 'table':
|
||||||
_delete_keys(temp_tgt_params)
|
target_ddl = (
|
||||||
diff_ddl = view_object.get_drop_sql(**temp_tgt_params)
|
view_object.get_sql_from_table_diff(**temp_tgt_params))
|
||||||
|
_delete_keys(temp_tgt_params)
|
||||||
|
diff_ddl = view_object.get_drop_sql(**temp_tgt_params)
|
||||||
|
elif node == 'view':
|
||||||
|
target_ddl = (
|
||||||
|
view_object.get_sql_from_view_diff(**temp_tgt_params))
|
||||||
|
temp_tgt_params.update(
|
||||||
|
{'drop_sql': True})
|
||||||
|
diff_ddl = (
|
||||||
|
view_object.get_sql_from_view_diff(**temp_tgt_params))
|
||||||
else:
|
else:
|
||||||
temp_tgt_params = copy.deepcopy(target_params)
|
temp_tgt_params = copy.deepcopy(target_params)
|
||||||
temp_tgt_params['oid'] = target_object_id
|
temp_tgt_params['oid'] = target_object_id
|
||||||
@@ -288,7 +308,7 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
|
|||||||
if 'scid' in target_params else 0,
|
if 'scid' in target_params else 0,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
if node == 'table':
|
if node == 'table' or node == 'view':
|
||||||
temp_src_params = copy.deepcopy(source_params)
|
temp_src_params = copy.deepcopy(source_params)
|
||||||
temp_tgt_params = copy.deepcopy(target_params)
|
temp_tgt_params = copy.deepcopy(target_params)
|
||||||
# Add submodules into the ignore keys so that directory
|
# Add submodules into the ignore keys so that directory
|
||||||
@@ -308,16 +328,27 @@ def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,
|
|||||||
|
|
||||||
temp_src_params['tid'] = source_object_id
|
temp_src_params['tid'] = source_object_id
|
||||||
temp_tgt_params['tid'] = target_object_id
|
temp_tgt_params['tid'] = target_object_id
|
||||||
temp_src_params['json_resp'] = \
|
|
||||||
temp_tgt_params['json_resp'] = False
|
|
||||||
|
|
||||||
source_ddl = \
|
if node == 'table':
|
||||||
view_object.get_sql_from_table_diff(**temp_src_params)
|
temp_src_params['json_resp'] = \
|
||||||
diff_dependencies = \
|
temp_tgt_params['json_resp'] = False
|
||||||
view_object.get_table_submodules_dependencies(
|
|
||||||
**temp_src_params)
|
source_ddl = \
|
||||||
target_ddl = \
|
view_object.get_sql_from_table_diff(**temp_src_params)
|
||||||
view_object.get_sql_from_table_diff(**temp_tgt_params)
|
diff_dependencies = \
|
||||||
|
view_object.get_table_submodules_dependencies(
|
||||||
|
**temp_src_params)
|
||||||
|
target_ddl = \
|
||||||
|
view_object.get_sql_from_table_diff(**temp_tgt_params)
|
||||||
|
elif node == 'view':
|
||||||
|
source_ddl = \
|
||||||
|
view_object.get_sql_from_view_diff(**temp_src_params)
|
||||||
|
diff_dependencies = \
|
||||||
|
view_object.get_view_submodules_dependencies(
|
||||||
|
**temp_src_params)
|
||||||
|
target_ddl = \
|
||||||
|
view_object.get_sql_from_view_diff(**temp_tgt_params)
|
||||||
|
|
||||||
diff_ddl = view_object.get_sql_from_submodule_diff(
|
diff_ddl = view_object.get_sql_from_submodule_diff(
|
||||||
source_params=temp_src_params,
|
source_params=temp_src_params,
|
||||||
target_params=temp_tgt_params,
|
target_params=temp_tgt_params,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||||||
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
||||||
<InputSelect
|
<InputSelect
|
||||||
options={databaseList}
|
options={databaseList}
|
||||||
optionsReloadBasis={databaseList?.length}
|
optionsReloadBasis={databaseList?.map ? _.join(databaseList.map((c)=>c.value), ',') : null}
|
||||||
onChange={changeDatabase}
|
onChange={changeDatabase}
|
||||||
value={selectedDatabase}
|
value={selectedDatabase}
|
||||||
controlProps={
|
controlProps={
|
||||||
@@ -114,7 +114,7 @@ export function InputComponent({ label, serverList, databaseList, schemaList, di
|
|||||||
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
<Grid item lg={3} md={3} sm={3} xs={3} className={classes.inputLabel}>
|
||||||
<InputSelect
|
<InputSelect
|
||||||
options={schemaList}
|
options={schemaList}
|
||||||
optionsReloadBasis={schemaList?.length}
|
optionsReloadBasis={schemaList?.map ? _.join(schemaList.map((c)=>c.value), ',') : null}
|
||||||
onChange={changeSchema}
|
onChange={changeSchema}
|
||||||
value={selectedSchema}
|
value={selectedSchema}
|
||||||
controlProps={
|
controlProps={
|
||||||
|
|||||||
Reference in New Issue
Block a user