Modified schema diff tool to compare two databases instead of two schemas. Fixes #5126

pull/35/head
Akshay Joshi 2020-08-10 15:13:34 +05:30
parent 3672013ddc
commit 4f74609ecf
72 changed files with 1622 additions and 741 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 228 KiB

View File

@ -10,6 +10,7 @@ New features
************
| `Issue #3904 <https://redmine.postgresql.org/issues/3904>`_ - Replace charting library Flotr2 with ChartJS using React.
| `Issue #5126 <https://redmine.postgresql.org/issues/5126>`_ - Modified schema diff tool to compare two databases instead of two schemas.
| `Issue #5610 <https://redmine.postgresql.org/issues/5610>`_ - Add a --yes command line option to setup-web.sh to allow non-interactive use.
Housekeeping

View File

@ -4,29 +4,29 @@
`Schema Diff`:index:
********************
**Schema Diff** is a feature that allows you to compare schema objects between
two database schemas. Use the *Tools* menu to access Schema Diff.
**Schema Diff** is a feature that allows you to compare objects between
two databases. Use the *Tools* menu to access Schema Diff.
The Schema Diff feature allows you to:
* Compare and synchronize the database schemas (from source to target).
* Compare and synchronize the database objects (from source to target).
* Visualize the differences between database schemas.
* Visualize the differences between database objects.
* List the differences in SQL statement for target schema objects.
* List the differences in SQL statement for target database objects.
* Generate synchronization scripts.
**Note** - The source and target databases must be of the same major
**Note** - The source and target database servers must be of the same major
version.
Click on *Schema Diff* under the *Tools* menu to open a selection panel. Choose
the source and target servers, databases, and schemas that will be
the source and target servers, and databases that will be
compared. 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
simultaneously. To close a copy of Schema Diff, click the *X* in the
upper-right hand corner of the tab bar.
.. image:: images/schema_diff_dialog.png
@ -44,7 +44,7 @@ The Schema Diff Object Comparison Panel
========================================
In the object comparison panel, you can select the source and target servers
of the same major version, databases, and schemas to be compared. You can
of the same major version, and databases to be compared. You can
select any server listed under the browser tree whether it is connected or
disconnected. If you select a server that is not connected then it will
prompt you for the password before using the server.
@ -52,40 +52,36 @@ prompt you for the password before using the server.
Next, select the databases that will be compared. The databases can be the
same or different (and within the same server or from different servers).
Lastly, select the source and target schemas which will be compared.
.. image:: images/schema_diff_compare_button.png
:alt: Schema diff compare button
:align: center
After you select servers, databases, and schemas, click on the
After you select servers, and databases, click on the
*Compare* button to obtain the *Comparison Result*.
.. image:: images/schema_diff_comparison_results.png
:alt: Schema diff comparison results
:align: center
Use the drop-down lists of Functions, Materialized Views, Tables,
Trigger Functions, Procedures, and Views to view the DDL statements of
all the schema objects.
Use the drop-down lists of Database Objects to view the DDL statements.
In the upper-right hand corner of the object comparison panel is a *Filter*
option that you can use to filter the schema objects based on the
option that you can use to filter the database objects based on the
following comparison criteria:
* Identical If the object is found in both schemas with the same SQL statement, then the comparison result is identical.
* Identical If the object is found in both databases with the same SQL statement, then the comparison result is identical.
* Different If the object is found in both schemas but have different SQL statements, then the comparison result is different.
* Different If the object is found in both databases but have different SQL statements, then the comparison result is different.
* Source Only If the object is found in source schema only and not in target schema, then the comparison result is source only.
* Source Only If the object is found in source database only and not in target database, then the comparison result is source only.
* Target Only If the object is found in target schema only and not in source schema, then the comparison result is target only.
* Target Only If the object is found in target database only and not in source database, then the comparison result is target only.
.. image:: images/schema_diff_filter_option.png
:alt: Schema diff filter option
:align: center
Click on any of the schema objects in the object comparison panel to
Click on any of the database objects in the object comparison panel to
display the DDL Statements of that object in the DDL Comparison panel.
@ -94,36 +90,32 @@ Schema Diff DDL Comparison Panel
The *DDL Comparison* panel displays three columns:
* The first column displays the DDL statement of the object from the source schema.
* The first column displays the DDL statement of the object from the source database.
* The second column displays the DDL statement of the object from the target schema.
* The second column displays the DDL statement of the object from the target database.
* The third column displays the difference in the SQL statement of the target schema object.
* The third column displays the difference in the SQL statement of the target database object.
.. image:: images/schema_diff_DDL_comparison.png
:alt: Schema diff DDL comparison
:align: center
You can review the DDL statements of all the schema objects to
You can review the DDL statements of all the database objects to
check for the differences in the SQL statements.
Also, you can generate the SQL script of the differences found in the
target schema object based on the SQL statement of the source schema
object. To generate the script, select the checkboxes of the schema
target database object based on the SQL statement of the source database
object. To generate the script, select the checkboxes of the database
objects in the object comparison panel and then click on the *Generate Script*
button in the upper-right hand corner of the object comparison panel.
.. image:: images/schema_diff_generate_script.png
:alt: Schema diff generate script
:align: center
Select the schema objects and click on the *Generate Script*
Select the database objects and click on the *Generate Script*
button to open the *Query Tool* in a new tab, with the difference
in the SQL statement displayed in the *Query Editor*.
If you have clicked on the schema object to check the difference
If you have clicked on the database object to check the difference
generated in the *DDL Comparison* Panel, and you have not selected the
checkbox of the schema object, PEM will open the *Query Tool* in a new
checkbox of the database object, pgAdmin will open the *Query Tool* in a new
tab, with the differences in the SQL statements displayed in the *Query Editor*.
You can also use the *Copy* button to copy the difference generated in
@ -133,4 +125,4 @@ the *DDL Comparison* panel.
:alt: Schema diff generate script query editor
:align: center
Apply the SQL Statement in the target schema to synchronize the schemas.
Apply the SQL Statement in the target database to synchronize the databases.

View File

@ -21,6 +21,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class CastModule(CollectionNodeModule):
@ -88,7 +90,7 @@ class CastModule(CollectionNodeModule):
blueprint = CastModule(__name__)
class CastView(PGChildNodeView):
class CastView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class CastView(PGChildNodeView)
@ -179,9 +181,12 @@ class CastView(PGChildNodeView):
'get_functions': [
{'post': 'get_functions'},
{'post': 'get_functions'}
]
],
'compare': [{'get': 'compare'}, {'get': 'compare'}]
})
keys_to_ignore = ['oid', 'id', 'oid-2']
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
@ -318,26 +323,41 @@ class CastView(PGChildNodeView):
:param cid: cast id
:return:
"""
status, res = self._fetch_properties(did, cid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, did, cid):
"""
This function fetch the properties of the
:param did:
:param cid:
:return:
"""
last_system_oid = 0 if self.blueprint.show_system_objects else \
self.datlastsysoid
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
cid=cid,
datlastsysoid=self.datlastsysoid,
datlastsysoid=last_system_oid,
showsysobj=self.blueprint.show_system_objects
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the cast information.")
)
return ajax_response(
response=res['rows'][0],
status=200
)
return True, res['rows'][0]
@check_precondition
def create(self, gid, sid, did):
@ -436,29 +456,42 @@ class CastView(PGChildNodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@staticmethod
def get_delete_data(cmd, cid, request_object):
"""
This function is used to get the data and cascade information.
:param cmd: Command
:param cid: Object ID
:param request_object: request object
:return:
"""
cascade = False
# Below will decide if it's simple drop or drop with cascade call
if cmd == 'delete':
# This is a cascade operation
cascade = True
if cid is None:
data = request_object.form if request_object.form else \
json.loads(request_object.data, encoding='utf-8')
else:
data = {'ids': [cid]}
return cascade, data
@check_precondition
def delete(self, gid, sid, did, cid=None):
def delete(self, gid, sid, did, cid=None, only_sql=False):
"""
This function will drop the cast object
:param cid: cast id
:param did: database id
:param sid: server id
:param gid: group id
:param only_sql:
:return:
"""
# Below will decide if it's simple drop or drop with cascade call
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
if cid is None:
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
else:
data = {'ids': [cid]}
# get the value of cascade and data
cascade, data = self.get_delete_data(self.cmd, cid, request)
for cid in data['ids']:
try:
@ -490,6 +523,11 @@ class CastView(PGChildNodeView):
casttarget=result['casttarget'],
cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -638,13 +676,14 @@ class CastView(PGChildNodeView):
)
@check_precondition
def sql(self, gid, sid, did, cid):
def sql(self, gid, sid, did, cid, json_resp=True):
"""
This function will generate sql for sql panel
:param gid: group id
:param sid: server id
:param did: database id
:param cid: cast id
:param json_resp:
:return:
"""
try:
@ -653,6 +692,7 @@ class CastView(PGChildNodeView):
cid=cid,
conn=self.conn
)
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(gettext(
@ -665,6 +705,9 @@ class CastView(PGChildNodeView):
"cast node."
))
if not json_resp:
return res
return ajax_response(response=res)
except Exception as e:
@ -706,5 +749,63 @@ class CastView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the casts for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
last_system_oid = 0
if self.manager.db_info is not None and did in self.manager.db_info:
last_system_oid = (self.manager.db_info[did])['datlastsysoid']
sql = render_template(
"/".join([self.template_path, 'nodes.sql']),
datlastsysoid=last_system_oid,
showsysobj=self.blueprint.show_system_objects
)
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(did, row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs:
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.get_sql(gid=gid, sid=sid, did=did, data=data,
cid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
cid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, cid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, CastView, 'Database')
CastView.register_node_view(blueprint)

View File

@ -20,6 +20,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class EventTriggerModule(CollectionNodeModule):
@ -94,7 +96,7 @@ class EventTriggerModule(CollectionNodeModule):
blueprint = EventTriggerModule(__name__)
class EventTriggerView(PGChildNodeView):
class EventTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class EventTriggerView(PGChildNodeView)
@ -172,6 +174,8 @@ class EventTriggerView(PGChildNodeView):
'fopts': [{'get': 'get_event_funcs'}, {'get': 'get_event_funcs'}]
})
keys_to_ignore = ['oid', 'xmin', 'oid-2', 'eventfuncoid']
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@ -325,6 +329,22 @@ class EventTriggerView(PGChildNodeView):
Returns:
"""
status, res = self._fetch_properties(did, etid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, did, etid):
"""
This function fetch the properties of the event trigger.
:param did:
:param etid:
:return:
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
@ -332,21 +352,17 @@ class EventTriggerView(PGChildNodeView):
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
gettext("Could not find the event trigger information.")
)
return False, gone(
gettext("Could not find the event trigger information."))
result = res['rows'][0]
result['is_sys_obj'] = (result['oid'] <= self.datlastsysoid)
result = self._formatter(result)
return ajax_response(
response=result,
status=200
)
return True, result
@check_precondition
def create(self, gid, sid, did):
@ -445,7 +461,7 @@ class EventTriggerView(PGChildNodeView):
# Most probably this is due to error
if not isinstance(sql, str):
return sql
sql = sql.strip('\n').strip(' ')
if sql != "":
status, res = self.conn.execute_scalar(sql)
if not status:
@ -480,8 +496,31 @@ class EventTriggerView(PGChildNodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@staticmethod
def get_delete_data(cmd, etid, request_object):
"""
This function is used to get the data and cascade information.
:param cmd: Command
:param etid: Object ID
:param request_object: request object
:return:
"""
cascade = False
# Below will decide if it's simple drop or drop with cascade call
if cmd == 'delete':
# This is a cascade operation
cascade = True
if etid is None:
data = request_object.form if request_object.form else \
json.loads(request_object.data, encoding='utf-8')
else:
data = {'ids': [etid]}
return cascade, data
@check_precondition
def delete(self, gid, sid, did, etid=None):
def delete(self, gid, sid, did, etid=None, only_sql=False):
"""
This function will delete an existing event trigger object.
@ -490,23 +529,13 @@ class EventTriggerView(PGChildNodeView):
sid: Server ID
did: Database ID
etid: Event trigger ID
only_sql:
Returns:
"""
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
if etid is None:
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
else:
data = {'ids': [etid]}
# get the value of cascade and data
cascade, data = self.get_delete_data(self.cmd, etid, request)
try:
for etid in data['ids']:
@ -534,6 +563,11 @@ class EventTriggerView(PGChildNodeView):
"/".join([self.template_path, self._DELETE_SQL]),
name=name, cascade=cascade
)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -572,7 +606,7 @@ class EventTriggerView(PGChildNodeView):
# Most probably this is due to error
if not isinstance(sql, str):
return sql
sql = sql.strip('\n').strip(' ')
sql = re.sub('\n{2,}', '\n\n', sql)
if sql == '':
sql = "--modified SQL"
@ -657,10 +691,10 @@ class EventTriggerView(PGChildNodeView):
"/".join([self.template_path, self._GRANT_SQL]),
data=data
)
return sql
return sql.strip('\n').strip(' ')
@check_precondition
def sql(self, gid, sid, did, etid):
def sql(self, gid, sid, did, etid, json_resp=True):
"""
This function will generate sql to show in the sql pane for the
selected event trigger node.
@ -670,6 +704,7 @@ class EventTriggerView(PGChildNodeView):
sid: Server ID
did: Database ID
etid: Event trigger ID
json_resp:
Returns:
@ -722,6 +757,9 @@ class EventTriggerView(PGChildNodeView):
sql = sql_header + sql
sql = re.sub('\n{2,}', '\n\n', sql)
if not json_resp:
return sql
return ajax_response(response=sql)
@check_precondition
@ -791,5 +829,62 @@ class EventTriggerView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the event triggers for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
last_system_oid = 0
if self.manager.db_info is not None and did in self.manager.db_info:
last_system_oid = (self.manager.db_info[did])['datlastsysoid']
sql = render_template(
"/".join([self.template_path, 'nodes.sql']),
datlastsysoid=last_system_oid,
showsysobj=self.blueprint.show_system_objects
)
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(did, row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs:
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql = self.get_sql(data=data, etid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
etid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, etid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, EventTriggerView, 'Database')
EventTriggerView.register_node_view(blueprint)

View File

@ -3,6 +3,9 @@
{% if (data.eventfunname and data.eventfunname != o_data.eventfunname) or
(data.eventname and data.eventname != o_data.eventname) or
(data.when and data.when != o_data.when) %}
-- WARNING:
-- We have found the difference in either of Trigger Function, Event or WHEN
-- so we need to drop the existing event trigger first and re-create it.
DROP EVENT TRIGGER IF EXISTS {{ conn|qtIdent(o_data.name) }};
CREATE EVENT TRIGGER {{ conn|qtIdent(data.name) if data.name else conn|qtIdent(o_data.name) }} ON {{ data.eventname if data.eventname else o_data.eventname }}

View File

@ -21,6 +21,8 @@ from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, internal_server_error, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class ExtensionModule(CollectionNodeModule):
@ -75,7 +77,7 @@ class ExtensionModule(CollectionNodeModule):
blueprint = ExtensionModule(__name__)
class ExtensionView(PGChildNodeView):
class ExtensionView(PGChildNodeView, SchemaDiffObjectCompare):
"""
This is a class for extension nodes which inherits the
properties and methods from NodeView class and define
@ -173,7 +175,7 @@ class ExtensionView(PGChildNodeView):
for row in rset['rows']:
res.append(
self.blueprint.generate_browser_node(
row['eid'],
row['oid'],
did,
row['name'],
'icon-extension'
@ -199,7 +201,7 @@ class ExtensionView(PGChildNodeView):
for row in rset['rows']:
return make_json_response(
data=self.blueprint.generate_browser_node(
row['eid'],
row['oid'],
did,
row['name'],
'icon-extension'
@ -214,23 +216,37 @@ class ExtensionView(PGChildNodeView):
"""
Fetch the properties of a single extension and render in properties tab
"""
status, res = self._fetch_properties(did, eid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, did, eid):
"""
This function fetch the properties of the extension.
:param did:
:param eid:
:return:
"""
SQL = render_template("/".join(
[self.template_path, self._PROPERTIES_SQL]), eid=eid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the extension information.")
)
res['rows'][0]['is_sys_obj'] = (
res['rows'][0]['eid'] <= self.datlastsysoid)
return ajax_response(
response=res['rows'][0],
status=200
)
res['rows'][0]['oid'] <= self.datlastsysoid)
return True, res['rows'][0]
@check_precondition
def create(self, gid, sid, did):
@ -278,7 +294,7 @@ class ExtensionView(PGChildNodeView):
for row in rset['rows']:
return jsonify(
node=self.blueprint.generate_browser_node(
row['eid'],
row['oid'],
did,
row['name'],
'icon-extension'
@ -316,7 +332,7 @@ class ExtensionView(PGChildNodeView):
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, eid=None):
def delete(self, gid, sid, did, eid=None, only_sql=False):
"""
This function will drop/drop cascade a extension object
"""
@ -355,6 +371,11 @@ class ExtensionView(PGChildNodeView):
SQL = render_template("/".join(
[self.template_path, self._DELETE_SQL]
), name=name, cascade=cascade)
# Used for schema diff tool
if only_sql:
return SQL
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -452,7 +473,7 @@ class ExtensionView(PGChildNodeView):
)
@check_precondition
def sql(self, gid, sid, did, eid):
def sql(self, gid, sid, did, eid, json_resp=True):
"""
This function will generate sql for the sql panel
"""
@ -477,6 +498,9 @@ class ExtensionView(PGChildNodeView):
display_comments=True
)
if not json_resp:
return SQL
return ajax_response(response=SQL)
@check_precondition
@ -515,6 +539,57 @@ class ExtensionView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the extensions for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
sql = render_template("/".join([self.template_path,
'properties.sql']))
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(did, row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.getSQL(gid=gid, sid=sid, did=did, data=data,
eid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
eid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, eid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, ExtensionView, 'Database')
# Register and add ExtensionView as blueprint
ExtensionView.register_node_view(blueprint)

View File

@ -4,5 +4,5 @@ SELECT x.extname from pg_extension x
WHERE x.oid = {{ eid }}::oid
{% endif %}
{% if name %}
DROP EXTENSION {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
DROP EXTENSION {{ conn|qtIdent(name) }}{% if cascade %} CASCADE{% endif %};
{% endif %}

View File

@ -1,6 +1,6 @@
{#===================Fetch properties of each extension by name or oid===================#}
SELECT
x.oid AS eid, pg_get_userbyid(extowner) AS owner,
x.oid, pg_get_userbyid(extowner) AS owner,
x.extname AS name, n.nspname AS schema,
x.extrelocatable AS relocatable, x.extversion AS version,
e.comment

View File

@ -23,6 +23,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class ForeignDataWrapperModule(CollectionNodeModule):
@ -98,7 +100,7 @@ class ForeignDataWrapperModule(CollectionNodeModule):
blueprint = ForeignDataWrapperModule(__name__)
class ForeignDataWrapperView(PGChildNodeView):
class ForeignDataWrapperView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class ForeignDataWrapperView(PGChildNodeView)
@ -196,6 +198,8 @@ class ForeignDataWrapperView(PGChildNodeView):
'get_validators': [{}, {'get': 'get_validators'}]
})
keys_to_ignore = ['oid', 'oid-2', 'fdwhandler', 'fdwvalidator']
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@ -280,7 +284,7 @@ class ForeignDataWrapperView(PGChildNodeView):
for row in r_set['rows']:
res.append(
self.blueprint.generate_browser_node(
row['fdwoid'],
row['oid'],
did,
row['name'],
icon="icon-foreign_data_wrapper"
@ -312,7 +316,7 @@ class ForeignDataWrapperView(PGChildNodeView):
for row in r_set['rows']:
return make_json_response(
data=self.blueprint.generate_browser_node(
row['fdwoid'],
row['oid'],
did,
row['name'],
icon="icon-foreign_data_wrapper"
@ -335,23 +339,40 @@ class ForeignDataWrapperView(PGChildNodeView):
did: Database ID
fid: foreign data wrapper ID
"""
status, res = self._fetch_properties(fid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, fid):
"""
This function fetch the properties of the FDW.
:param fid:
:return:
"""
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
fid=fid, conn=self.conn
)
status, res = self.conn.execute_dict(sql)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the foreign data"
" wrapper information.")
)
res['rows'][0]['is_sys_obj'] = (
res['rows'][0]['fdwoid'] <= self.datlastsysoid)
res['rows'][0]['oid'] <= self.datlastsysoid)
if res['rows'][0]['fdwoptions'] is not None:
res['rows'][0]['fdwoptions'] = tokenize_options(
@ -362,9 +383,10 @@ class ForeignDataWrapperView(PGChildNodeView):
sql = render_template("/".join([self.template_path, self._ACL_SQL]),
fid=fid
)
status, fdw_acl_res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=fdw_acl_res)
return False, internal_server_error(errormsg=fdw_acl_res)
for row in fdw_acl_res['rows']:
privilege = parse_priv_from_db(row)
@ -373,10 +395,7 @@ class ForeignDataWrapperView(PGChildNodeView):
else:
res['rows'][0][row['deftype']] = [privilege]
return ajax_response(
response=res['rows'][0],
status=200
)
return True, res['rows'][0]
@check_precondition
def create(self, gid, sid, did):
@ -438,7 +457,7 @@ class ForeignDataWrapperView(PGChildNodeView):
for row in r_set['rows']:
return jsonify(
node=self.blueprint.generate_browser_node(
row['fdwoid'],
row['oid'],
did,
row['name'],
icon='icon-foreign_data_wrapper'
@ -484,8 +503,31 @@ class ForeignDataWrapperView(PGChildNodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@staticmethod
def get_delete_data(cmd, fid, request_object):
"""
This function is used to get the data and cascade information.
:param cmd: Command
:param fid: Object ID
:param request_object: request object
:return:
"""
cascade = False
# Below will decide if it's simple drop or drop with cascade call
if cmd == 'delete':
# This is a cascade operation
cascade = True
if fid is None:
data = request_object.form if request_object.form else \
json.loads(request_object.data, encoding='utf-8')
else:
data = {'ids': [fid]}
return cascade, data
@check_precondition
def delete(self, gid, sid, did, fid=None):
def delete(self, gid, sid, did, fid=None, only_sql=False):
"""
This function will delete the selected foreign data wrapper node.
@ -494,19 +536,10 @@ class ForeignDataWrapperView(PGChildNodeView):
sid: Server ID
did: Database ID
fid: foreign data wrapper ID
only_sql:
"""
if fid is None:
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
else:
data = {'ids': [fid]}
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
# get the value of cascade and data
cascade, data = self.get_delete_data(self.cmd, fid, request)
for fid in data['ids']:
try:
@ -537,6 +570,11 @@ class ForeignDataWrapperView(PGChildNodeView):
name=name,
cascade=cascade,
conn=self.conn)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -717,14 +755,15 @@ class ForeignDataWrapperView(PGChildNodeView):
is_valid_changed_options=is_valid_changed_options,
conn=self.conn
)
return sql, data['name'] if 'name' in data else old_data['name']
return sql.strip('\n'), \
data['name'] if 'name' in data else old_data['name']
else:
sql = self._get_create_sql(data)
return sql, data['name']
return sql.strip('\n'), data['name']
@check_precondition
def sql(self, gid, sid, did, fid):
def sql(self, gid, sid, did, fid, json_resp=True):
"""
This function will generate sql to show it in sql pane
for the selected foreign data wrapper node.
@ -734,6 +773,7 @@ class ForeignDataWrapperView(PGChildNodeView):
sid: Server ID
did: Database ID
fid: Foreign data wrapper ID
json_resp:
"""
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
@ -795,6 +835,9 @@ class ForeignDataWrapperView(PGChildNodeView):
sql = sql_header + sql
if not json_resp:
return sql.strip('\n')
return ajax_response(response=sql.strip('\n'))
@check_precondition
@ -896,5 +939,56 @@ class ForeignDataWrapperView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the FDWs for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
sql = render_template("/".join([self.template_path,
'properties.sql']))
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.get_sql(gid=gid, sid=sid, did=did, data=data,
fid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
fid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, fid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, ForeignDataWrapperView, 'Database')
ForeignDataWrapperView.register_node_view(blueprint)

View File

@ -23,6 +23,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class ForeignServerModule(CollectionNodeModule):
@ -98,7 +100,7 @@ class ForeignServerModule(CollectionNodeModule):
blueprint = ForeignServerModule(__name__)
class ForeignServerView(PGChildNodeView):
class ForeignServerView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class ForeignServerView(PGChildNodeView)
@ -187,6 +189,8 @@ class ForeignServerView(PGChildNodeView):
'dependent': [{'get': 'dependents'}]
})
keys_to_ignore = ['oid', 'oid-2', 'fdwid']
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@ -269,7 +273,7 @@ class ForeignServerView(PGChildNodeView):
for row in r_set['rows']:
res.append(
self.blueprint.generate_browser_node(
row['fsrvid'],
row['oid'],
fid,
row['name'],
icon="icon-foreign_server"
@ -305,7 +309,7 @@ class ForeignServerView(PGChildNodeView):
return make_json_response(
data=self.blueprint.generate_browser_node(
row['fsrvid'],
row['oid'],
fid,
row['name'],
icon="icon-foreign_server"
@ -328,22 +332,36 @@ class ForeignServerView(PGChildNodeView):
fid: foreign data wrapper ID
fsid: foreign server ID
"""
status, res = self._fetch_properties(fsid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, fsid):
"""
This function fetch the properties of the Foreign server.
:param fsid:
:return:
"""
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
fsid=fsid, conn=self.conn)
status, res = self.conn.execute_dict(sql)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the foreign server information.")
)
res['rows'][0]['is_sys_obj'] = (
res['rows'][0]['fsrvid'] <= self.datlastsysoid)
res['rows'][0]['oid'] <= self.datlastsysoid)
if res['rows'][0]['fsrvoptions'] is not None:
res['rows'][0]['fsrvoptions'] = tokenize_options(
@ -354,9 +372,8 @@ class ForeignServerView(PGChildNodeView):
fsid=fsid
)
status, fs_rv_acl_res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=fs_rv_acl_res)
return False, internal_server_error(errormsg=fs_rv_acl_res)
for row in fs_rv_acl_res['rows']:
privilege = parse_priv_from_db(row)
@ -365,10 +382,7 @@ class ForeignServerView(PGChildNodeView):
else:
res['rows'][0][row['deftype']] = [privilege]
return ajax_response(
response=res['rows'][0],
status=200
)
return True, res['rows'][0]
@check_precondition
def create(self, gid, sid, did, fid):
@ -439,7 +453,7 @@ class ForeignServerView(PGChildNodeView):
return jsonify(
node=self.blueprint.generate_browser_node(
r_set['rows'][0]['fsrvid'],
r_set['rows'][0]['oid'],
fid,
r_set['rows'][0]['name'],
icon="icon-foreign_server"
@ -488,8 +502,31 @@ class ForeignServerView(PGChildNodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@staticmethod
def get_delete_data(cmd, fsid, request_object):
"""
This function is used to get the data and cascade information.
:param cmd: Command
:param fsid: Object ID
:param request_object: request object
:return:
"""
cascade = False
# Below will decide if it's simple drop or drop with cascade call
if cmd == 'delete':
# This is a cascade operation
cascade = True
if fsid is None:
data = request_object.form if request_object.form else \
json.loads(request_object.data, encoding='utf-8')
else:
data = {'ids': [fsid]}
return cascade, data
@check_precondition
def delete(self, gid, sid, did, fid, fsid=None):
def delete(self, gid, sid, did, fid, fsid=None, only_sql=False):
"""
This function will delete the selected foreign server node.
@ -499,20 +536,10 @@ class ForeignServerView(PGChildNodeView):
did: Database ID
fid: foreign data wrapper ID
fsid: foreign server ID
only_sql:
"""
if fsid is None:
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
else:
data = {'ids': [fsid]}
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
# get the value of cascade and data
cascade, data = self.get_delete_data(self.cmd, fsid, request)
try:
for fsid in data['ids']:
@ -542,6 +569,11 @@ class ForeignServerView(PGChildNodeView):
self._DELETE_SQL]),
name=name, cascade=cascade,
conn=self.conn)
# Used for schema diff tool
if only_sql:
return sql.strip('\n')
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -674,7 +706,8 @@ class ForeignServerView(PGChildNodeView):
is_valid_changed_options=is_valid_changed_options,
conn=self.conn
)
return sql, data['name'] if 'name' in data else old_data['name']
return sql.strip('\n'), \
data['name'] if 'name' in data else old_data['name']
else:
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
@ -704,7 +737,7 @@ class ForeignServerView(PGChildNodeView):
return sql, data['name']
@check_precondition
def sql(self, gid, sid, did, fid, fsid):
def sql(self, gid, sid, did, fid, fsid, json_resp=True):
"""
This function will generate sql to show it in sql pane for the
selected foreign server node.
@ -715,6 +748,7 @@ class ForeignServerView(PGChildNodeView):
did: Database ID
fid: Foreign data wrapper ID
fsid: Foreign server ID
json_resp:
"""
sql = render_template("/".join([self.template_path,
@ -728,6 +762,9 @@ class ForeignServerView(PGChildNodeView):
gettext("Could not find the foreign server information.")
)
if fid is None and 'fdwid' in res['rows'][0]:
fid = res['rows'][0]['fdwid']
is_valid_options = False
if res['rows'][0]['fsrvoptions'] is not None:
res['rows'][0]['fsrvoptions'] = tokenize_options(
@ -782,6 +819,9 @@ class ForeignServerView(PGChildNodeView):
sql = sql_header + sql
if not json_resp:
return sql.strip('\n')
return ajax_response(response=sql.strip('\n'))
@check_precondition
@ -836,5 +876,57 @@ class ForeignServerView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the FDWs for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
sql = render_template("/".join([self.template_path,
'properties.sql']))
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
fdw_id = kwargs.get('fdwid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.get_sql(gid=gid, sid=sid, did=did, data=data,
fid=fdw_id, fsid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did, fid=fdw_id,
fsid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, fid=fdw_id,
fsid=oid, json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, ForeignServerView, 'Database')
ForeignServerView.register_node_view(blueprint)

View File

@ -101,7 +101,7 @@ define('pgadmin.node.foreign_server', [
// Defining model for foreign server node
model: pgAdmin.Browser.Node.Model.extend({
idAttribute: 'fsrvid',
idAttribute: 'oid',
defaults: {
name: undefined,
fsrvtype: undefined,
@ -135,7 +135,7 @@ define('pgadmin.node.foreign_server', [
);
},
},{
id: 'fsrvid', label: gettext('OID'), cell: 'string',
id: 'oid', label: gettext('OID'), cell: 'string',
type: 'text', mode: ['properties'],
},{
id: 'fsrvowner', label: gettext('Owner'), type: 'text',

View File

@ -5,8 +5,8 @@ FROM pg_foreign_data_wrapper fdw
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
WHERE fdw.oid={{fdwid}}::oid
{% else %}
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
array_to_string(srvoptions, ',') AS fsrvoptions,
SELECT srv.oid, srvname as name, srvfdw as fdwid, srvtype as fsrvtype, srvversion as fsrvversion,
fdw.fdwname as fdwname, description, array_to_string(srvoptions, ',') AS fsrvoptions,
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
FROM pg_foreign_server srv
LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
@ -23,4 +23,4 @@ WHERE srvfdw={{fid}}::oid
WHERE srv.oid={{fsid}}::oid
{% endif %}
ORDER BY srvname;
{% endif %}
{% endif %}

View File

@ -6,7 +6,7 @@ FROM pg_foreign_data_wrapper fdw
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
WHERE fdw.oid={{fdwid}}::oid
{% else %}
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
SELECT srv.oid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
array_to_string(srvoptions, ',') AS fsrvoptions,
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
FROM pg_foreign_server srv
@ -24,4 +24,4 @@ WHERE srvfdw={{fid}}::oid
WHERE srv.oid={{fsid}}::oid
{% endif %}
ORDER BY srvname;
{% endif %}
{% endif %}

View File

@ -1,10 +1,37 @@
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data %}
{% if (data.fsrvtype is defined and data.fsrvtype != o_data.fsrvtype) or (data.fdwname is defined and data.fdwname != o_data.fdwname) %}
{% set fsrvtype = o_data.fsrvtype %}
{% set fdwname = o_data.fdwname %}
{% if data.fsrvtype is defined %}
{% set fsrvtype = data.fsrvtype %}
{% endif %}
{% if data.fdwname is defined %}
{% set fdwname = data.fdwname %}
{% endif %}
-- WARNING:
-- We have found the difference in SERVER TYPE OR FOREIGN DATA WRAPPER
-- so we need to drop the existing foreign server first and re-create it.
DROP SERVER {{ conn|qtIdent(o_data.name) }};
CREATE SERVER {{ conn|qtIdent(o_data.name) }}{% if data.fsrvtype or o_data.fsrvtype %}
TYPE {{ fsrvtype|qtLiteral }}{% endif %}{% if o_data.fsrvversion %}
VERSION {{ o_data.fsrvversion|qtLiteral }}{%-endif %}{% if o_data.fdwname %}
FOREIGN DATA WRAPPER {{ conn|qtIdent(fdwname) }}{% endif %}{% if o_data.fsrvoptions %}
OPTIONS ({% for variable in o_data.fsrvoptions %}{% if loop.index != 1 %}, {% endif %}
{{ conn|qtIdent(variable.fsrvoption) }} {{ variable.fsrvvalue|qtLiteral }}{% endfor %}){% endif %};
{% else %}
{# ============= Update foreign server name ============= #}
{% if data.name != o_data.name %}
ALTER SERVER {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% endif %}
{# ============= Update foreign server owner ============= #}
{% if data.fsrvowner and data.fsrvowner != o_data.fsrvowner %}
@ -13,7 +40,7 @@ ALTER SERVER {{ conn|qtIdent(data.name) }}
{% endif %}
{# ============= Update foreign server version ============= #}
{% if data.fsrvversion and data.fsrvversion != o_data.fsrvversion %}
{% if data.fsrvversion is defined and data.fsrvversion != o_data.fsrvversion %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
VERSION {{ data.fsrvversion|qtLiteral }};

View File

@ -23,6 +23,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class UserMappingModule(CollectionNodeModule):
@ -114,7 +116,7 @@ class UserMappingModule(CollectionNodeModule):
blueprint = UserMappingModule(__name__)
class UserMappingView(PGChildNodeView):
class UserMappingView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class UserMappingView(PGChildNodeView)
@ -204,6 +206,8 @@ class UserMappingView(PGChildNodeView):
'dependent': [{'get': 'dependents'}]
})
keys_to_ignore = ['oid', 'oid-2', 'fdwid', 'fsid']
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@ -287,7 +291,7 @@ class UserMappingView(PGChildNodeView):
for row in r_set['rows']:
res.append(
self.blueprint.generate_browser_node(
row['um_oid'],
row['oid'],
fsid,
row['name'],
icon="icon-user_mapping"
@ -322,7 +326,7 @@ class UserMappingView(PGChildNodeView):
for row in r_set['rows']:
return make_json_response(
data=self.blueprint.generate_browser_node(
row['um_oid'],
row['oid'],
fsid,
row['name'],
icon="icon-user_mapping"
@ -346,32 +350,44 @@ class UserMappingView(PGChildNodeView):
fsid: Foreign server ID
umid: User mapping ID
"""
status, res = self._fetch_properties(umid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, umid):
"""
This function fetch the properties of the User Mapping.
:param umid:
:return:
"""
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
umid=umid, conn=self.conn)
status, res = self.conn.execute_dict(sql)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the user mapping information.")
)
res['rows'][0]['is_sys_obj'] = (
res['rows'][0]['um_oid'] <= self.datlastsysoid)
res['rows'][0]['oid'] <= self.datlastsysoid)
if res['rows'][0]['umoptions'] is not None:
res['rows'][0]['umoptions'] = tokenize_options(
res['rows'][0]['umoptions'],
'umoption', 'umvalue'
)
return ajax_response(
response=res['rows'][0],
status=200
)
return True, res['rows'][0]
@check_precondition
def create(self, gid, sid, did, fid, fsid):
@ -443,7 +459,7 @@ class UserMappingView(PGChildNodeView):
for row in r_set['rows']:
return jsonify(
node=self.blueprint.generate_browser_node(
row['um_oid'],
row['oid'],
fsid,
row['name'],
icon='icon-user_mapping'
@ -492,8 +508,31 @@ class UserMappingView(PGChildNodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@staticmethod
def get_delete_data(cmd, umid, request_object):
"""
This function is used to get the data and cascade information.
:param cmd: Command
:param umid: Object ID
:param request_object: request object
:return:
"""
cascade = False
# Below will decide if it's simple drop or drop with cascade call
if cmd == 'delete':
# This is a cascade operation
cascade = True
if umid is None:
data = request_object.form if request_object.form else \
json.loads(request_object.data, encoding='utf-8')
else:
data = {'ids': [umid]}
return cascade, data
@check_precondition
def delete(self, gid, sid, did, fid, fsid, umid=None):
def delete(self, gid, sid, did, fid, fsid, **kwargs):
"""
This function will delete the selected user mapping node.
@ -503,20 +542,15 @@ class UserMappingView(PGChildNodeView):
did: Database ID
fid: foreign data wrapper ID
fsid: foreign server ID
umid: User mapping ID
"""
if umid is None:
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
else:
data = {'ids': [umid]}
**kwargs:
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
"""
umid = kwargs.get('umid', None)
only_sql = kwargs.get('only_sql', False)
# get the value of cascade and data
cascade, data = self.get_delete_data(self.cmd, umid, request)
try:
for umid in data['ids']:
@ -564,6 +598,11 @@ class UserMappingView(PGChildNodeView):
self._DELETE_SQL]),
data=data, name=name, cascade=cascade,
conn=self.conn)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -707,7 +746,7 @@ class UserMappingView(PGChildNodeView):
return sql, data['name']
@check_precondition
def sql(self, gid, sid, did, fid, fsid, umid):
def sql(self, gid, sid, did, fid, fsid, **kwargs):
"""
This function will generate sql to show it in sql pane for
the selected user mapping node.
@ -718,8 +757,10 @@ class UserMappingView(PGChildNodeView):
did: Database ID
fid: Foreign data wrapper ID
fsid: Foreign server ID
umid: User mapping ID
kwargs:
"""
umid = kwargs.get('umid')
json_resp = kwargs.get('json_resp', True)
sql = render_template("/".join([self.template_path,
self._PROPERTIES_SQL]),
@ -732,6 +773,9 @@ class UserMappingView(PGChildNodeView):
gettext("Could not find the user mapping information.")
)
if fsid is None and 'fsid' in res['rows'][0]:
fsid = res['rows'][0]['fsid']
is_valid_options = False
if res['rows'][0]['umoptions'] is not None:
res['rows'][0]['umoptions'] = tokenize_options(
@ -767,6 +811,9 @@ class UserMappingView(PGChildNodeView):
sql = sql_header + sql
if not json_resp:
return sql.strip('\n')
return ajax_response(response=sql.strip('\n'))
@check_precondition
@ -810,5 +857,57 @@ class UserMappingView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the FDWs for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
sql = render_template("/".join([self.template_path,
'properties.sql']))
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs:
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
fid = kwargs.get('fdwid')
fsid = kwargs.get('fsid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.get_sql(data=data, fsid=fsid, umid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did, fid=fid,
fsid=fsid, umid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, fid=fid, fsid=fsid,
umid=oid, json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, UserMappingView, 'Database')
UserMappingView.register_node_view(blueprint)

View File

@ -5,5 +5,5 @@ SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_dat
{% endif %}
{# ============= Drop/Delete cascade user mapping ============= #}
{% if name and data %}
DROP USER MAPPING FOR {% if data.name == "CURRENT_USER" or data.name == "PUBLIC" %}{{ data.name }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} SERVER {{ conn|qtIdent(name) }}
{% endif %}
DROP USER MAPPING FOR {% if data.name == "CURRENT_USER" or data.name == "PUBLIC" %}{{ data.name }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} SERVER {{ conn|qtIdent(name) }};
{% endif %}

View File

@ -4,10 +4,15 @@ SELECT srv.oid as fsrvid, srvname as name
FROM pg_foreign_server srv
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
WHERE srv.oid = {{fserid}}::oid
{% endif %}
{% if fsid or umid %}
SELECT u.umid AS um_oid, u.usename as name, array_to_string(u.umoptions, ',') AS umoptions
{% elif fsid or umid %}
SELECT u.umid AS oid, u.usename AS name, u.srvid AS fsid, array_to_string(u.umoptions, ',') AS umoptions, fs.srvfdw AS fdwid
FROM pg_user_mappings u
LEFT JOIN pg_foreign_server fs ON fs.oid = u.srvid
{% if fsid %} WHERE u.srvid = {{fsid}}::oid {% endif %} {% if umid %} WHERE u.umid= {{umid}}::oid {% endif %}
ORDER BY 2;
{% else %}
SELECT u.umid AS oid, u.usename AS name, u.srvid AS fsid, array_to_string(u.umoptions, ',') AS umoptions, fs.srvfdw AS fdwid
FROM pg_user_mappings u
LEFT JOIN pg_foreign_server fs ON fs.oid = u.srvid
ORDER BY 2;
{% endif %}

View File

@ -104,7 +104,7 @@ define('pgadmin.node.foreign_data_wrapper', [
// Defining model for foreign data wrapper node
model: pgBrowser.Node.Model.extend({
idAttribute: 'fdwoid',
idAttribute: 'oid',
defaults: {
name: undefined,
fdwowner: undefined,
@ -138,7 +138,7 @@ define('pgadmin.node.foreign_data_wrapper', [
);
},
},{
id: 'fdwoid', label: gettext('OID'), cell: 'string',
id: 'oid', label: gettext('OID'), cell: 'string',
type: 'text', mode: ['properties'],
},{
id: 'fdwowner', label: gettext('Owner'), type: 'text',

View File

@ -1,5 +1,5 @@
{# ============= Get all the properties of foreign data wrapper ============= #}
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, description,
SELECT fdw.oid, fdwname as name, fdwhandler, fdwvalidator, description,
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl,
CASE
-- EPAS in redwood mode, concatenation of a string with NULL results as the original string

View File

@ -18,7 +18,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
VALIDATOR {{ data.fdwvalue }};
{% endif %}
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
{% if (data.fdwvalue == '' or data.fdwvalue == None) and data.fdwvalue != o_data.fdwvalue %}
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
NO VALIDATOR;
@ -29,7 +29,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
HANDLER {{ data.fdwhan }};
{% endif %}
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
{% if (data.fdwhan == '' or data.fdwhan == None) and data.fdwhan != o_data.fdwhan %}
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
NO HANDLER;

View File

@ -1,5 +1,5 @@
{# ============= Get all the properties of foreign data wrapper ============= #}
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, description,
SELECT fdw.oid, fdwname as name, fdwhandler, fdwvalidator, description,
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl,
CASE
-- EPAS in redwood mode, concatenation of a string with NULL results as the original string

View File

@ -18,7 +18,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
VALIDATOR {{ data.fdwvalue }};
{% endif %}
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
{% if (data.fdwvalue == '' or data.fdwvalue == None) and data.fdwvalue != o_data.fdwvalue %}
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
NO VALIDATOR;
@ -29,7 +29,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
HANDLER {{ data.fdwhan }};
{% endif %}
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
{% if (data.fdwhan == '' or data.fdwhan == None) and data.fdwhan != o_data.fdwhan %}
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
NO HANDLER;

View File

@ -23,6 +23,8 @@ from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
class LanguageModule(CollectionNodeModule):
@ -105,7 +107,7 @@ class LanguageModule(CollectionNodeModule):
blueprint = LanguageModule(__name__)
class LanguageView(PGChildNodeView):
class LanguageView(PGChildNodeView, SchemaDiffObjectCompare):
"""
class LanguageView(PGChildNodeView)
@ -338,6 +340,22 @@ class LanguageView(PGChildNodeView):
did: Database ID
lid: Language ID
"""
status, res = self._fetch_properties(did, lid)
if not status:
return res
return ajax_response(
response=res,
status=200
)
def _fetch_properties(self, did, lid):
"""
This function fetch the properties of the extension.
:param did:
:param lid:
:return:
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
lid=lid
@ -345,10 +363,10 @@ class LanguageView(PGChildNodeView):
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
return False, gone(
gettext("Could not find the language information.")
)
@ -361,7 +379,7 @@ class LanguageView(PGChildNodeView):
)
status, result = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=result)
return False, internal_server_error(errormsg=result)
# if no acl found then by default add public
if res['rows'][0]['acl'] is None:
@ -396,10 +414,7 @@ class LanguageView(PGChildNodeView):
res['rows'][0]['seclabels'] = seclabels
return ajax_response(
response=res['rows'][0],
status=200
)
return True, res['rows'][0]
@check_precondition
def update(self, gid, sid, did, lid):
@ -498,7 +513,7 @@ class LanguageView(PGChildNodeView):
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, lid=None):
def delete(self, gid, sid, did, lid=None, only_sql=False):
"""
This function will drop the language object
@ -507,6 +522,7 @@ class LanguageView(PGChildNodeView):
sid: Server ID
did: Database ID
lid: Language ID
only_sql:
"""
if lid is None:
data = request.form if request.form else json.loads(
@ -538,8 +554,12 @@ class LanguageView(PGChildNodeView):
"/".join([self.template_path, self._DELETE_SQL]),
lname=lname, cascade=cascade, conn=self.conn
)
status, res = self.conn.execute_scalar(sql)
# Used for schema diff tool
if only_sql:
return sql
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
@ -690,7 +710,7 @@ class LanguageView(PGChildNodeView):
)
@check_precondition
def sql(self, gid, sid, did, lid):
def sql(self, gid, sid, did, lid, json_resp=True):
"""
This function will generate sql to show in the sql pane for the
selected language node.
@ -700,6 +720,7 @@ class LanguageView(PGChildNodeView):
sid: Server ID
did: Database ID
lid: Language ID
json_resp:
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
@ -755,6 +776,9 @@ class LanguageView(PGChildNodeView):
data=old_data, conn=self.conn
)
if not json_resp:
return sql.strip('\n')
return ajax_response(response=sql.strip('\n'))
@check_precondition
@ -793,5 +817,54 @@ class LanguageView(PGChildNodeView):
status=200
)
@check_precondition
def fetch_objects_to_compare(self, sid, did):
"""
This function will fetch the list of all the event triggers for
specified database id.
:param sid: Server Id
:param did: Database Id
:return:
"""
res = dict()
sql = render_template("/".join([self.template_path,
'properties.sql']))
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
status, data = self._fetch_properties(did, row['oid'])
if status:
res[row['name']] = data
return res
def get_sql_from_diff(self, **kwargs):
"""
This function is used to get the DDL/DML statements.
:param kwargs:
:return:
"""
gid = kwargs.get('gid')
sid = kwargs.get('sid')
did = kwargs.get('did')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
sql, name = self.get_sql(data=data, lid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
lid=oid, only_sql=True)
else:
sql = self.sql(gid=gid, sid=sid, did=did, lid=oid,
json_resp=False)
return sql
SchemaDiffRegistry(blueprint.node_type, LanguageView, 'Database')
LanguageView.register_node_view(blueprint)

View File

@ -4,5 +4,5 @@
{% endif %}
{# ============= Drop the language ============= #}
{% if lname %}
DROP LANGUAGE {{ conn|qtIdent(lname) }} {% if cascade %}CASCADE{% endif%};
{% endif %}
DROP LANGUAGE {{ conn|qtIdent(lname) }}{% if cascade %} CASCADE{% endif%};
{% endif %}

View File

@ -1,6 +1,28 @@
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% import 'macros/security.macros' as SECLABEL %}
{% if data %}
{# ============= Check for Schema Diff Tool ============= #}
{% if (data.trusted and data.trusted != o_data.trusted) or (data.lanproc and data.lanproc != o_data.lanproc) or (data.laninl and data.laninl != o_data.laninl) or (data.lanval and data.lanval != o_data.lanval) %}
-- WARNING:
-- We have found the difference in either of TRUSTED, HANDLER, INLINE or VALIDATOR,
-- so we need to drop the existing language first and re-create it.
DROP LANGUAGE {{ conn|qtIdent(o_data.name) }} CASCADE;
{% if data.trusted is defined %}{% set tmp_trusted = data.trusted %}{% else %}{% set tmp_trusted = o_data.trusted %}{% endif %}
{% if data.lanproc is defined %}{% set tmp_lanproc = data.lanproc %}{% else %}{% set tmp_lanproc = o_data.lanproc %}{% endif %}
{% if data.laninl is defined %}{% set tmp_laninl = data.laninl %}{% else %}{% set tmp_laninl = o_data.laninl %}{% endif %}
{% if data.lanval is defined %}{% set tmp_lanval = data.lanval %}{% else %}{% set tmp_lanval = o_data.lanval %}{% endif %}
CREATE{% if tmp_trusted %} TRUSTED{% endif %} PROCEDURAL LANGUAGE {{ conn|qtIdent(o_data.name) }}
{% if tmp_lanproc %}
HANDLER {{ conn|qtIdent(tmp_lanproc) }}
{% endif %}
{% if tmp_laninl %}
INLINE {{ conn|qtIdent(tmp_laninl) }}
{% endif %}
{% if tmp_lanval %}
VALIDATOR {{ conn|qtIdent(tmp_lanval) }}
{% endif %};
{% endif %}
{# ============= Update language name ============= #}
{% if data.name != o_data.name %}
ALTER LANGUAGE {{ conn|qtIdent(o_data.name) }}
@ -59,4 +81,4 @@ COMMENT ON LANGUAGE {{ conn|qtIdent(data.name) }}
{{ SECLABEL.APPLY(conn, 'PROCEDURAL LANGUAGE', data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -701,10 +701,8 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
coid: Collation ID
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
SQL = render_template("/".join([self.template_path,
@ -720,9 +718,6 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
data = res['rows'][0]
if diff_schema:
data['schema'] = diff_schema
SQL = render_template("/".join([self.template_path,
self._CREATE_SQL]),
data=data, conn=self.conn)
@ -821,21 +816,15 @@ class CollationView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, coid=oid,
json_resp=False)

View File

@ -723,10 +723,8 @@ AND relkind != 'c'))"""
did: Database Id
scid: Schema Id
doid: Domain Id
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
SQL = render_template("/".join([self.template_path,
@ -742,9 +740,6 @@ AND relkind != 'c'))"""
data = res['rows'][0]
if diff_schema:
data['basensp'] = diff_schema
# Get Type Length and Precision
data.update(self._parse_type(data['fulltype']))
@ -954,12 +949,9 @@ AND relkind != 'c'))"""
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, scid=scid,
data=data, doid=oid,
is_schema_diff=True)
@ -967,9 +959,6 @@ AND relkind != 'c'))"""
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, doid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, doid=oid,
json_resp=False)

View File

@ -832,10 +832,8 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
did: Database Id
scid: Schema Id
foid: Foreign Table Id
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
status, data = self._fetch_properties(gid, sid, did, scid, foid,
@ -846,9 +844,6 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
return gone(
gettext("The specified foreign table could not be found."))
if diff_schema:
data['basensp'] = diff_schema
col_data = []
for c in data['columns']:
if ('inheritedfrom' not in c) or (c['inheritedfrom'] is None):
@ -1494,12 +1489,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, foid=oid,
is_schema_diff=True)
@ -1507,9 +1499,6 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, foid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, foid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, foid=oid,
json_resp=False)

View File

@ -205,6 +205,8 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
'compare': [{'get': 'compare'}, {'get': 'compare'}]
})
keys_to_ignore = ['oid', 'oid-2', 'schema']
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
@ -903,10 +905,8 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param cfgid: FTS Configuration id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
try:
@ -932,22 +932,6 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
"FTS Configuration node.")
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
self._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, diff_schema)
if not json_resp:
return res
@ -1032,21 +1016,15 @@ class FtsConfigurationView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, cfgid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, cfgid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, cfgid=oid,
json_resp=False)

View File

@ -192,6 +192,8 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
{'get': 'fetch_templates'}]
})
keys_to_ignore = ['oid', 'oid-2', 'schema']
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
@ -798,10 +800,8 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param dcid: FTS Dictionary id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
sql = render_template(
@ -847,9 +847,6 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
# Replace schema oid with schema name
res['rows'][0]['schema'] = schema
if diff_schema:
res['rows'][0]['schema'] = diff_schema
sql = render_template("/".join([self.template_path,
self._CREATE_SQL]),
data=res['rows'][0],
@ -943,21 +940,15 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, dcid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, dcid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, dcid=oid,
json_resp=False)

View File

@ -201,6 +201,8 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
{'get': 'headline_functions'}]
})
keys_to_ignore = ['oid', 'oid-2', 'schema']
def _init_(self, **kwargs):
"""
Method is used to initialize the FtsParserView and it's base view.
@ -848,10 +850,8 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param pid: fts tempate id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
try:
@ -878,22 +878,6 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
)
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
self._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, diff_schema)
if not json_resp:
return res
@ -978,21 +962,15 @@ class FtsParserView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, scid=scid,
data=data, pid=oid)
else:
if drop_sql:
sql = self.delete(gid=gid, sid=sid, did=did,
scid=scid, pid=oid, only_sql=True)
elif diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pid=oid,
json_resp=False)

View File

@ -187,6 +187,8 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
'get_init': [{'get': 'get_init'}, {'get': 'get_init'}]
})
keys_to_ignore = ['oid', 'oid-2', 'schema']
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
@ -735,10 +737,8 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
:param did: database id
:param scid: schema id
:param tid: fts tempate id
:param diff_schema: Target Schema for schema diff
:param json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
sql = render_template(
@ -762,22 +762,6 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
"FTS Template node.")
)
# Used for schema diff tool
if diff_schema:
data = {'schema': scid}
# Fetch schema name from schema oid
sql = render_template("/".join([self.template_path,
self._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, diff_schema)
if not json_resp:
return res
@ -858,21 +842,15 @@ class FtsTemplateView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name = self.get_sql(gid=gid, sid=sid, did=did, 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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
json_resp=False)

View File

@ -221,7 +221,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
keys_to_ignore = ['oid', 'proowner', 'typnsp', 'xmin', 'prokind',
'proisagg', 'pronamespace', 'proargdefaults',
'prorettype', 'proallargtypes', 'proacl', 'oid-2']
'prorettype', 'proallargtypes', 'proacl', 'oid-2',
'prolang']
@property
def required_args(self):
@ -1085,8 +1086,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
did: Database Id
scid: Schema Id
fnid: Function Id
json_resp:
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
resp_data = self._fetch_properties(gid, sid, did, scid, fnid)
@ -1131,8 +1132,6 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
status, res = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=res)
elif diff_schema:
res['rows'][0]['nspname'] = diff_schema
# Add newline and tab before each argument to format
name_with_default_args = self.qtIdent(
@ -1170,9 +1169,6 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
status, res = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=res)
elif diff_schema:
res['rows'][0]['nspname'] = diff_schema
resp_data['pronamespace'] = diff_schema
# Add newline and tab before each argument to format
name_with_default_args = self.qtIdent(
@ -1823,12 +1819,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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)
@ -1842,9 +1835,6 @@ 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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, fnid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, fnid=oid,
json_resp=False)

View File

@ -12,7 +12,7 @@ import re
from functools import wraps
import simplejson as json
from flask import render_template, request, jsonify
from flask import render_template, make_response, request, jsonify
from flask_babelex import gettext as _
import pgadmin.browser.server_groups.servers.databases as database
@ -584,15 +584,10 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
data = kwargs.get('data')
pkgid = kwargs.get('pkgid', None)
sqltab = kwargs.get('sqltab', False)
diff_schema = kwargs.get('diff_schema', None)
if diff_schema:
data['schema'] = diff_schema
else:
data['schema'] = self.schema
is_schema_diff = kwargs.get('is_schema_diff', None)
if pkgid is not None and not sqltab:
return self.get_sql_with_pkgid(scid, pkgid, data, diff_schema)
return self.get_sql_with_pkgid(scid, pkgid, data, is_schema_diff)
else:
# To format privileges coming from client
if 'pkgacl' in data:
@ -618,7 +613,15 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
data[key]['deleted'] = parse_priv_to_db(
data[key]['deleted'], self.acl)
def get_sql_with_pkgid(self, scid, pkgid, data, diff_schema):
def get_sql_with_pkgid(self, scid, pkgid, data, is_schema_diff):
"""
:param scid:
:param pkgid:
:param data:
:param is_schema_diff:
:return:
"""
required_args = [
u'name'
]
@ -666,7 +669,7 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
sql = render_template("/".join([self.template_path,
self._UPDATE_SQL]),
data=data, o_data=old_data, conn=self.conn,
is_schema_diff=diff_schema)
is_schema_diff=is_schema_diff)
return sql, data['name'] if 'name' in data else old_data['name']
@check_precondition(action="sql")
@ -680,10 +683,10 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
pkgid: Package ID
diff_schema: Schema diff target schema name
is_schema_diff:
json_resp: json response or plain text response
"""
diff_schema = kwargs.get('diff_schema', None)
is_schema_diff = kwargs.get('is_schema_diff', None)
json_resp = kwargs.get('json_resp', True)
try:
@ -717,8 +720,11 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
res['rows'][0].setdefault(row['deftype'], []).append(priv)
result = res['rows'][0]
sql, name = self.getSQL(data=result, scid=scid, pkgid=pkgid,
sqltab=True, diff_schema=diff_schema)
sqltab=True,
is_schema_diff=is_schema_diff)
# Most probably this is due to error
if not isinstance(sql, str):
return sql
@ -841,23 +847,17 @@ class PackageView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pkgid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, pkgid=oid,
json_resp=False)
is_schema_diff=True, json_resp=False)
return sql

View File

@ -699,10 +699,8 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
seid: Sequence ID
diff_schema: Schema diff target schema name
json_resp: json response or plain text response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
sql = render_template(
@ -734,9 +732,6 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare):
result = res['rows'][0]
if diff_schema:
result['schema'] = diff_schema
result = self._formatter(result, scid, seid)
sql, name = self.get_SQL(gid, sid, did, result, scid)
# Most probably this is due to error
@ -952,20 +947,14 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, seid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, seid=oid,
json_resp=False)

View File

@ -670,10 +670,8 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
syid: Synonym ID
diff_schema:
json_resp:
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
SQL = render_template("/".join([self.template_path,
@ -690,9 +688,6 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare):
gettext('The specified synonym could not be found.')
)
if diff_schema:
data['schema'] = diff_schema
SQL = render_template("/".join([self.template_path,
self._CREATE_SQL]),
data=data, conn=self.conn, comment=True)
@ -781,21 +776,14 @@ class SynonymView(PGChildNodeView, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, syid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, syid=oid,
json_resp=False)

View File

@ -1249,8 +1249,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
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
diff_schema = kwargs['diff_schema'] if 'diff_schema' in kwargs else\
None
if diff_data:
return self._fetch_sql(did, scid, tid, diff_data, json_resp)
@ -1273,9 +1271,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
if status:
data = res['rows'][0]
if diff_schema:
data['schema'] = diff_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)
@ -1662,13 +1657,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
:param tid: Table Id
:return: Table dataset
"""
sub_modules = ['index', 'rule', 'trigger']
if self.manager.server_type == 'ppas' and \
self.manager.version >= 120000:
sub_modules.append('compound_trigger')
if self.manager.version >= 90500:
sub_modules.append('row_security_policy')
if tid:
status, data = self._fetch_properties(did, scid, tid)
@ -1704,16 +1692,15 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
# Get sub module data of a specified table for object
# comparison
self._get_sub_module_data_for_compare(sid, did, scid, data,
row, sub_modules)
row)
res[row['name']] = data
return res
def _get_sub_module_data_for_compare(self, sid, did, scid, data,
row, sub_modules):
def _get_sub_module_data_for_compare(self, sid, did, scid, data, row):
# Get sub module data of a specified table for object
# comparison
for module in sub_modules:
for module in self.tables_sub_modules:
module_view = SchemaDiffRegistry.get_node_view(module)
if module_view.blueprint.server_type is None or \
self.manager.server_type in \
@ -1723,6 +1710,76 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
oid=None)
data[module] = sub_data
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 == 'index':
template_path = self.index_template_path
elif 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
elif module_name == 'row_security_policy':
template_path = self.row_security_policies_template_path
return template_path
@BaseTableView.check_precondition
def get_table_submodules_dependencies(self, **kwargs):
"""
This function is used to get the dependencies of table and it's
submodules.
:param kwargs:
:return:
"""
tid = kwargs['tid']
table_dependencies = []
table_deps = self.get_dependencies(self.conn, tid, where=None,
show_system_objects=None,
is_schema_diff=True)
if len(table_deps) > 0:
table_dependencies.extend(table_deps)
# Fetch foreign key referenced table which is considered as
# dependency.
status, fkey_deps = fkey_utils.get_fkey_dependencies(self.conn, tid)
if not status:
return internal_server_error(errormsg=fkey_deps)
if len(fkey_deps) > 0:
table_dependencies.extend(fkey_deps)
# Iterate all the submodules of the table and fetch the dependencies.
for module in self.tables_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=tid)
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:
table_dependencies.extend(result)
# Remove the same table from the dependency list
for item in table_dependencies:
if 'oid' in item and item['oid'] == tid:
table_dependencies.remove(item)
return table_dependencies
SchemaDiffRegistry(blueprint.node_type, TableView)
TableView.register_node_view(blueprint)

View File

@ -887,7 +887,6 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
tid = kwargs.get('tid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
@ -927,22 +926,17 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
data['columns'] = self._column_details(tid, columns)
data = trigger_definition(data)
sql = self._check_and_add_compound_trigger(tid, data,
diff_schema)
sql = self._check_and_add_compound_trigger(tid, data)
return sql
def _check_and_add_compound_trigger(self, tid, data, diff_schema):
def _check_and_add_compound_trigger(self, tid, data):
"""
This get compound trigger and check for disable.
:param tid: Table Id.
:param data: Data.
:param diff_schema: schema diff check.
"""
if diff_schema:
data['schema'] = diff_schema
sql, name = compound_trigger_utils.get_sql(self.conn,
data,
tid,
@ -1007,7 +1001,6 @@ class CompoundTriggerView(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 = ''
@ -1017,8 +1010,7 @@ class CompoundTriggerView(PGChildNodeView, SchemaDiffObjectCompare):
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
oid=source['oid'])
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],

View File

@ -15,6 +15,8 @@ from pgadmin.utils.ajax import internal_server_error
from pgadmin.utils.exception import ObjectGone, ExecuteError
from functools import wraps
FKEY_PROPERTIES_SQL = 'properties.sql'
def get_template_path(f):
"""
@ -47,7 +49,7 @@ def get_foreign_keys(conn, tid, fkid=None, template_path=None):
"""
sql = render_template("/".join(
[template_path, 'properties.sql']), tid=tid, cid=fkid)
[template_path, FKEY_PROPERTIES_SQL]), tid=tid, cid=fkid)
status, result = conn.execute_dict(sql)
if not status:
@ -286,7 +288,7 @@ def _get_properties_for_fk_const(tid, fkid, data, template_path, conn):
conn: Connection.
"""
name = data['name'] if 'name' in data else None
sql = render_template("/".join([template_path, 'properties.sql']),
sql = render_template("/".join([template_path, FKEY_PROPERTIES_SQL]),
tid=tid, cid=fkid)
status, res = conn.execute_dict(sql)
if not status:
@ -357,3 +359,29 @@ def _checks_for_schema_diff(table, schema, data):
if 'remote_table' not in data:
data['remote_table'] = None
@get_template_path
def get_fkey_dependencies(conn, tid, template_path=None):
"""
This function is used to get the references table of all the foreign
keys of the given table.
:param conn:
:param tid:
:param template_path:
:return:
"""
deps = []
sql = render_template("/".join(
[template_path, FKEY_PROPERTIES_SQL]), tid=tid)
status, result = conn.execute_dict(sql)
if not status:
return status, internal_server_error(errormsg=result)
for fk in result['rows']:
ref_name = fk['refnsp'] + '.' + fk['reftab']
deps.append({'type': 'table', 'name': ref_name})
return True, deps

View File

@ -849,7 +849,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
tid = kwargs.get('tid')
idx = kwargs.get('idx')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
create_mode = kwargs.get('create_mode', None)
drop_req = kwargs.get('drop_req', False)
sql = ''
@ -864,10 +864,9 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
sql = sql.strip('\n').strip(' ')
elif diff_schema:
schema = diff_schema
elif create_mode:
sql = index_utils.get_reverse_engineered_sql(
self.conn, schema=schema,
self.conn, schema=self.schema,
table=self.table, did=did, tid=tid, idx=idx,
datlastsysoid=self.datlastsysoid,
template_path=None, with_header=False)
@ -1077,7 +1076,6 @@ class IndexesView(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 = ''
@ -1087,7 +1085,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
scid=src_params['scid'],
tid=src_params['tid'],
idx=source['oid'],
diff_schema=target_schema)
create_mode=True)
elif comp_status == 'target_only':
diff = self.delete(gid=1, sid=tgt_params['sid'],
did=tgt_params['did'], scid=tgt_params['scid'],
@ -1113,7 +1111,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
scid=src_params['scid'],
tid=src_params['tid'],
idx=source['oid'],
diff_schema=target_schema,
create_mode=True,
drop_req=True)
else:
diff = self.get_sql_from_index_diff(sid=tgt_params['sid'],

View File

@ -606,7 +606,6 @@ class RowSecurityView(PGChildNodeView):
tid = kwargs.get('tid')
oid = kwargs.get('plid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_req = kwargs.get('drop_req', False)
sql = ''
@ -619,11 +618,9 @@ class RowSecurityView(PGChildNodeView):
sql = sql.strip('\n').strip(' ')
elif diff_schema:
schema = diff_schema
else:
sql = row_security_policies_utils.get_reverse_engineered_sql(
self.conn, schema=schema, table=self.table, scid=scid,
self.conn, schema=self.schema, table=self.table, scid=scid,
plid=oid, datlastsysoid=self.datlastsysoid, with_header=False)
drop_sql = ''
@ -684,7 +681,6 @@ 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 = ''
@ -694,8 +690,7 @@ class RowSecurityView(PGChildNodeView):
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
plid=source['oid'],
diff_schema=target_schema)
plid=source['oid'])
elif comp_status == 'target_only':
diff = self.delete(gid=1,
sid=tgt_params['sid'],
@ -722,8 +717,7 @@ class RowSecurityView(PGChildNodeView):
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
plid=source['oid'],
diff_schema=target_schema)
plid=source['oid'])
return delete_sql + diff
diff = self.get_sql_from_diff(gid=tgt_params['gid'],

View File

@ -531,8 +531,6 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
tid = kwargs.get('tid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
source_schema = kwargs.get('source_schema', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if drop_sql:
@ -551,43 +549,19 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
)
res_data = parse_rule_definition(res)
sql = ''
if data:
if source_schema and 'statements' in data:
# Replace the source schema with the target schema
data['statements'] = data['statements'].replace(
source_schema, diff_schema)
old_data = res_data
sql = render_template(
"/".join([self.template_path, self._UPDATE_SQL]),
data=data, o_data=old_data
)
else:
RuleView._check_schema_diff(diff_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(diff_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 diff_schema:
if 'statements' in res_data:
# Replace the source schema with the target schema
res_data['statements'] = \
res_data['statements'].replace(
res_data['schema'], diff_schema)
res_data['schema'] = diff_schema
@check_precondition
def dependents(self, gid, sid, did, scid, tid, rid):
"""
@ -675,7 +649,6 @@ class RuleView(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 = ''
@ -685,8 +658,7 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
oid=source['oid'])
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],
@ -708,8 +680,6 @@ class RuleView(PGChildNodeView, SchemaDiffObjectCompare):
scid=tgt_params['scid'],
tid=tgt_params['tid'],
oid=target['oid'],
source_schema=source['schema'],
diff_schema=target_schema,
data=diff_dict)
return diff

View File

@ -51,13 +51,16 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid')}
group_name = kwargs.get('group_name')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
status, target_schema = self.get_schema(**target_params)
if not status:
return internal_server_error(errormsg=target_schema)
source_tables = {}
target_tables = {}
source_tables = self.fetch_tables(**source_params)
target_tables = self.fetch_tables(**target_params)
if 'scid' in source_params and source_params['scid'] is not None:
source_tables = self.fetch_tables(**source_params)
if 'scid' in target_params and target_params['scid'] is not None:
target_tables = self.fetch_tables(**target_params)
# If both the dict have no items then return None.
if not (source_tables or target_tables) or (
@ -67,11 +70,11 @@ 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,
node_label=self.blueprint.collection_label,
group_name=group_name,
ignore_whitespaces=ignore_whitespaces,
ignore_keys=self.keys_to_ignore)
@ -239,7 +242,6 @@ 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')
@ -297,7 +299,6 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
target_params=target_params,
source=dict1[item],
target=None,
target_schema=target_schema,
comp_status='source_only'
)
@ -311,7 +312,6 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
target_params=target_params,
source=None,
target=dict2[item],
target_schema=target_schema,
comp_status='target_only'
)
@ -329,7 +329,6 @@ class SchemaDiffTableCompare(SchemaDiffObjectCompare):
target_params=target_params,
source=dict1[key],
target=dict2[key],
target_schema=target_schema,
comp_status='different',
parent_source_data=source,
parent_target_data=target

View File

@ -805,7 +805,6 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
tid = kwargs.get('tid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
@ -825,8 +824,6 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
only_sql=True)
else:
schema = self.schema
if diff_schema:
schema = diff_schema
SQL = trigger_utils.get_reverse_engineered_sql(
self.conn, schema=schema, table=self.table, tid=tid,
trid=oid, datlastsysoid=self.datlastsysoid,
@ -997,7 +994,6 @@ 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 = ''
@ -1007,8 +1003,7 @@ class TriggerView(PGChildNodeView, SchemaDiffObjectCompare):
did=src_params['did'],
scid=src_params['scid'],
tid=src_params['tid'],
oid=source['oid'],
diff_schema=target_schema)
oid=source['oid'])
elif comp_status == 'target_only':
diff = self.get_sql_from_diff(gid=tgt_params['gid'],
sid=tgt_params['sid'],

View File

@ -145,6 +145,13 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
# Supported ACL for columns
self.column_acl = ['a', 'r', 'w', 'x']
# Submodule list for schema diff
self.tables_sub_modules = ['index', 'rule', 'trigger']
if server_type == 'ppas' and ver >= 120000:
self.tables_sub_modules.append('compound_trigger')
if ver >= 90500:
self.tables_sub_modules.append('row_security_policy')
return f(*args, **kwargs)
return wrap

View File

@ -1363,10 +1363,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
did: Database ID
scid: Schema ID
tid: Type ID
diff_schema: Target Schema for schema diff
json_resp: True then return json response
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
SQL = render_template(
@ -1386,9 +1384,6 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
# Making copy of output for future use
data = dict(res['rows'][0])
if diff_schema:
data['schema'] = diff_schema
SQL = render_template("/".join([self.template_path, self._ACL_SQL]),
scid=scid, tid=tid)
status, acl = self.conn.execute_dict(SQL)
@ -1518,21 +1513,15 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, tid=oid,
json_resp=False)

View File

@ -1305,9 +1305,7 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
"""
This function will generate sql to render into the sql panel
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
display_comments = True
if not json_resp:
@ -1329,11 +1327,6 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
)
result = res['rows'][0]
if diff_schema:
result['definition'] = result['definition'].replace(
result['schema'],
diff_schema)
result['schema'] = diff_schema
# sending result to formtter
frmtd_reslt = self.formatter(result)
@ -1631,12 +1624,9 @@ class ViewNode(PGChildNodeView, VacuumSettings, SchemaDiffObjectCompare):
scid = kwargs.get('scid')
oid = kwargs.get('oid')
data = kwargs.get('data', None)
diff_schema = kwargs.get('diff_schema', None)
drop_sql = kwargs.get('drop_sql', False)
if data:
if diff_schema:
data['schema'] = diff_schema
sql, name_or_error = self.getSQL(gid, sid, did, data, oid)
if sql.find('DROP VIEW') != -1:
sql = gettext("""
@ -1649,9 +1639,6 @@ 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 diff_schema:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, vid=oid,
diff_schema=diff_schema, json_resp=False)
else:
sql = self.sql(gid=gid, sid=sid, did=did, scid=scid, vid=oid,
json_resp=False)
@ -1885,7 +1872,6 @@ class MViewNode(ViewNode, VacuumSettings):
"""
This function will generate sql to render into the sql panel
"""
diff_schema = kwargs.get('diff_schema', None)
json_resp = kwargs.get('json_resp', True)
display_comments = True
@ -1899,12 +1885,6 @@ class MViewNode(ViewNode, VacuumSettings):
if not status:
return result
if diff_schema:
result['definition'] = result['definition'].replace(
result['schema'],
diff_schema)
result['schema'] = diff_schema
# merge vacuum lists into one
vacuum_table = [item for item in result['vacuum_table']
if

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -70,4 +70,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -72,4 +72,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -72,4 +72,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -70,4 +70,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -72,4 +72,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace',
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -76,4 +76,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
'pg_synonym', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN CASE WHEN tg.tgpackageoid != 0 THEN 'Tc'::text ELSE 'Tr'::text END
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -76,4 +76,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
'pg_synonym', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, pg_get_expr(ad.adbin, ad.adrelid) as adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -74,4 +74,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
'pg_synonym'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -1,4 +1,4 @@
SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc,
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
WHEN tg.oid IS NOT NULL THEN 'Tr'::text
WHEN ty.oid IS NOT NULL THEN CASE WHEN ty.typtype = 'd' THEN 'd'::text ELSE 'Ty'::text END
@ -76,4 +76,18 @@ refclassid IN ( SELECT oid FROM pg_class WHERE relname IN
'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper',
'pg_collation', 'pg_ts_config', 'pg_ts_dict', 'pg_ts_parser', 'pg_ts_template', 'pg_extension',
'pg_synonym', 'pg_policy'))
ORDER BY refclassid, cl.relkind
UNION
SELECT DISTINCT dep.deptype, dep.refclassid, dep.refobjid, cl.relkind, ad.adbin, ad.adsrc,
CASE WHEN cl.relkind IS NOT NULL THEN CASE WHEN cl.relkind = 'r' THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') ELSE cl.relkind END
ELSE '' END AS type,
NULL AS ownertable,
CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || COALESCE('.' || att.attname, '')
ELSE cl.relname END AS refname,
nsc.nspname AS nspname, '0' AS is_inherits, '0' AS is_inherited
FROM pg_depend dep
LEFT JOIN pg_class cl ON dep.refobjid=cl.oid
LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum
WHERE dep.objid IN (SELECT oid FROM pg_rewrite WHERE ev_class={{object_id}}) AND cl.relkind not in ('v', 'm')
ORDER BY refclassid, relkind

View File

@ -37,7 +37,8 @@ class TestDependenciesSql(SQLTemplateTestBase):
"dependencies.sql")
template = file_as_template(template_file)
sql = template.render(
where_clause="WHERE dep.objid=%s::oid" % self.table_id)
where_clause="WHERE dep.objid=%s::oid" % self.table_id,
object_id=self.table_id)
return sql

View File

@ -451,7 +451,7 @@ class PGChildNodeView(NodeView):
)
def get_dependencies(self, conn, object_id, where=None,
show_system_objects=None):
show_system_objects=None, is_schema_diff=False):
"""
This function is used to fetch the dependencies for the selected node.
@ -459,6 +459,8 @@ class PGChildNodeView(NodeView):
conn: Connection object
object_id: Object Id of the selected node.
where: where clause for the sql query (optional)
show_system_objects: System object status
is_schema_diff: True when function gets called from schema diff.
Returns: Dictionary of dependencies for the selected node.
"""
@ -473,10 +475,11 @@ class PGChildNodeView(NodeView):
where_clause = where
query = render_template("/".join([sql_path, 'dependencies.sql']),
where_clause=where_clause)
where_clause=where_clause,
object_id=object_id)
# fetch the dependency for the selected object
dependencies = self.__fetch_dependency(conn, query,
show_system_objects)
dependencies = self.__fetch_dependency(
conn, query, show_system_objects, is_schema_diff)
# fetch role dependencies
if where_clause.find('subid') < 0:
@ -534,13 +537,16 @@ class PGChildNodeView(NodeView):
return dependents
def __fetch_dependency(self, conn, query, show_system_objects=None):
def __fetch_dependency(self, conn, query, show_system_objects=None,
is_schema_diff=False):
"""
This function is used to fetch the dependency for the selected node.
Args:
conn: Connection object
query: sql query to fetch dependencies/dependents
show_system_objects: System object status
is_schema_diff: True when function gets called from schema diff.
Returns: Dictionary of dependency for the selected node.
"""
@ -597,6 +603,9 @@ class PGChildNodeView(NodeView):
type_str = row['type']
dep_str = row['deptype']
nsp_name = row['nspname']
object_id = None
if 'refobjid' in row:
object_id = row['refobjid']
ref_name = ''
if nsp_name is not None:
@ -658,31 +667,43 @@ class PGChildNodeView(NodeView):
if _ref_name is not None:
ref_name += _ref_name
dep_type = ''
if show_system_objects is None:
show_system_objects = self.blueprint.show_system_objects
if dep_str[0] in dep_types:
# if dep_type is present in the dep_types dictionary, but it's
# value is None then it requires special handling.
if dep_types[dep_str[0]] is None:
if dep_str[0] == 'i':
if show_system_objects:
dep_type = 'internal'
else:
continue
elif dep_str[0] == 'p':
dep_type = 'pin'
type_name = ''
else:
dep_type = dep_types[dep_str[0]]
# If schema diff is set to True then we don't need to calculate
# field and also no need to add icon and field in the list.
if is_schema_diff and type_name != 'schema':
dependency.append(
{
'type': type_name,
'name': ref_name,
'oid': object_id
}
)
elif not is_schema_diff:
dep_type = ''
if show_system_objects is None:
show_system_objects = self.blueprint.show_system_objects
if dep_str[0] in dep_types:
# if dep_type is present in the dep_types dictionary,
# but it's value is None then it requires special
# handling.
if dep_types[dep_str[0]] is None:
if dep_str[0] == 'i':
if show_system_objects:
dep_type = 'internal'
else:
continue
elif dep_str[0] == 'p':
dep_type = 'pin'
type_name = ''
else:
dep_type = dep_types[dep_str[0]]
dependency.append(
{
'type': type_name,
'name': ref_name,
'field': dep_type,
'icon': icon,
}
)
dependency.append(
{
'type': type_name,
'name': ref_name,
'field': dep_type,
'icon': icon,
}
)
return dependency

View File

@ -11,6 +11,7 @@
import simplejson as json
import pickle
import random
import copy
from flask import Response, session, url_for, request
from flask import render_template, current_app as app
@ -61,7 +62,6 @@ class SchemaDiffModule(PgAdminModule):
'schema_diff.panel',
'schema_diff.servers',
'schema_diff.databases',
'schema_diff.schemas',
'schema_diff.compare',
'schema_diff.poll',
'schema_diff.ddl_compare',
@ -397,46 +397,14 @@ def databases(sid):
return make_json_response(data=res)
@blueprint.route(
'/schemas/<int:sid>/<int:did>',
methods=["GET"],
endpoint="schemas"
)
@login_required
def schemas(sid, did):
"""
This function will return the list of schemas for the specified
server id and database id.
"""
res = []
try:
view = SchemaDiffRegistry.get_node_view('schema')
server = Server.query.filter_by(id=sid).first()
response = view.nodes(gid=server.servergroup_id, sid=sid, did=did)
if response.status_code == 200:
schemas = json.loads(response.data)['data']
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/<int:trans_id>/<int:source_sid>/<int:source_did>/'
'<int:source_scid>/<int:target_sid>/<int:target_did>/<int:target_scid>',
'<int:target_sid>/<int:target_did>',
methods=["GET"],
endpoint="compare"
)
@login_required
def compare(trans_id, source_sid, source_did, source_scid,
target_sid, target_did, target_scid):
def compare(trans_id, source_sid, source_did, target_sid, target_did):
"""
This function will compare the two schemas.
"""
@ -463,33 +431,88 @@ def compare(trans_id, source_sid, source_did, source_scid,
pref = Preferences.module('schema_diff')
ignore_whitespaces = pref.preference('ignore_whitespaces').get()
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes()
node_percent = round(100 / len(all_registered_nodes))
# Fetch all the schemas of source and target database
# Compare them and get the status.
schema_result = fetch_compare_schemas(source_sid, source_did,
target_sid, target_did)
total_schema = len(schema_result['source_only']) + len(
schema_result['target_only']) + len(
schema_result['in_both_database'])
node_percent = round(100 / (total_schema * len(
SchemaDiffRegistry.get_registered_nodes())))
total_percent = 0
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}').\
format(gettext(view.blueprint.collection_label))
diff_model_obj.set_comparison_info(msg, total_percent)
# Update the message and total percentage in session object
update_session_diff_transaction(trans_id, session_obj,
diff_model_obj)
# Compare Database objects
comparison_schema_result, total_percent = \
compare_database_objects(
trans_id=trans_id, session_obj=session_obj,
source_sid=source_sid, source_did=source_did,
target_sid=target_sid, target_did=target_did,
diff_model_obj=diff_model_obj, total_percent=total_percent,
node_percent=node_percent,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
res = view.compare(source_sid=source_sid,
source_did=source_did,
source_scid=source_scid,
target_sid=target_sid,
target_did=target_did,
target_scid=target_scid,
ignore_whitespaces=ignore_whitespaces)
# Compare Schema objects
if 'source_only' in schema_result and \
len(schema_result['source_only']) > 0:
for item in schema_result['source_only']:
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=item['scid'], target_sid=target_sid,
target_did=target_did, target_scid=None,
schema_name=item['schema_name'],
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent,
ignore_whitespaces=ignore_whitespaces)
if res is not None:
comparison_result = comparison_result + res
total_percent = total_percent + node_percent
comparison_result = \
comparison_result + comparison_schema_result
msg = gettext("Successfully compare the specified schemas.")
if 'target_only' in schema_result and \
len(schema_result['target_only']) > 0:
for item in schema_result['target_only']:
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=None, target_sid=target_sid,
target_did=target_did, target_scid=item['scid'],
schema_name=item['schema_name'],
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
# Compare the two schema present in both the databases
if 'in_both_database' in schema_result and \
len(schema_result['in_both_database']) > 0:
for item in schema_result['in_both_database']:
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=item['src_scid'], target_sid=target_sid,
target_did=target_did, target_scid=item['tar_scid'],
schema_name=item['schema_name'],
diff_model_obj=diff_model_obj,
total_percent=total_percent,
node_percent=node_percent,
ignore_whitespaces=ignore_whitespaces)
comparison_result = \
comparison_result + comparison_schema_result
msg = gettext("Successfully compare the specified databases.")
total_percent = 100
diff_model_obj.set_comparison_info(msg, total_percent)
# Update the message and total percentage done in session object
@ -609,3 +632,169 @@ def check_version_compatibility(sid, tid):
return False, gettext('Source and Target database server must be of '
'the same major version.')
def get_schemas(sid, did):
"""
This function will return the list of schemas for the specified
server id and database id.
"""
try:
view = SchemaDiffRegistry.get_node_view('schema')
server = Server.query.filter_by(id=sid).first()
response = view.nodes(gid=server.servergroup_id, sid=sid, did=did)
schemas = json.loads(response.data)['data']
return schemas
except Exception as e:
app.logger.exception(e)
return None
def compare_database_objects(**kwargs):
"""
This function is used to compare the specified schema and their children.
:param kwargs:
:return:
"""
trans_id = kwargs.get('trans_id')
session_obj = kwargs.get('session_obj')
source_sid = kwargs.get('source_sid')
source_did = kwargs.get('source_did')
target_sid = kwargs.get('target_sid')
target_did = kwargs.get('target_did')
diff_model_obj = kwargs.get('diff_model_obj')
total_percent = kwargs.get('total_percent')
node_percent = kwargs.get('node_percent')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
comparison_result = []
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes(None,
'Database')
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}'). \
format(gettext(view.blueprint.collection_label))
diff_model_obj.set_comparison_info(msg, total_percent)
# Update the message and total percentage in session object
update_session_diff_transaction(trans_id, session_obj,
diff_model_obj)
res = view.compare(source_sid=source_sid,
source_did=source_did,
target_sid=target_sid,
target_did=target_did,
group_name=gettext('Database Objects'),
ignore_whitespaces=ignore_whitespaces)
if res is not None:
comparison_result = comparison_result + res
total_percent = total_percent + node_percent
return comparison_result, total_percent
def compare_schema_objects(**kwargs):
"""
This function is used to compare the specified schema and their children.
:param kwargs:
:return:
"""
trans_id = kwargs.get('trans_id')
session_obj = kwargs.get('session_obj')
source_sid = kwargs.get('source_sid')
source_did = kwargs.get('source_did')
source_scid = kwargs.get('source_scid')
target_sid = kwargs.get('target_sid')
target_did = kwargs.get('target_did')
target_scid = kwargs.get('target_scid')
schema_name = kwargs.get('schema_name')
diff_model_obj = kwargs.get('diff_model_obj')
total_percent = kwargs.get('total_percent')
node_percent = kwargs.get('node_percent')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
comparison_result = []
all_registered_nodes = SchemaDiffRegistry.get_registered_nodes()
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))
diff_model_obj.set_comparison_info(msg, total_percent)
# Update the message and total percentage in session object
update_session_diff_transaction(trans_id, session_obj,
diff_model_obj)
res = view.compare(source_sid=source_sid,
source_did=source_did,
source_scid=source_scid,
target_sid=target_sid,
target_did=target_did,
target_scid=target_scid,
group_name=gettext(schema_name),
ignore_whitespaces=ignore_whitespaces)
if res is not None:
comparison_result = comparison_result + res
total_percent = total_percent + node_percent
# if total_percent is more then 100 then set it to less then 100
if total_percent >= 100:
total_percent = 96
return comparison_result, total_percent
def fetch_compare_schemas(source_sid, source_did, target_sid, target_did):
"""
This function is used to fetch all the schemas of source and target
database and compare them.
:param source_sid:
:param source_did:
:param target_sid:
:param target_did:
:return:
"""
source_schemas = get_schemas(source_sid, source_did)
target_schemas = get_schemas(target_sid, target_did)
src_schema_dict = {item['label']: item['_id'] for item in source_schemas}
tar_schema_dict = {item['label']: item['_id'] for item in target_schemas}
dict1 = copy.deepcopy(src_schema_dict)
dict2 = copy.deepcopy(tar_schema_dict)
# Find the duplicate keys in both the dictionaries
dict1_keys = set(dict1.keys())
dict2_keys = set(dict2.keys())
intersect_keys = dict1_keys.intersection(dict2_keys)
# Keys that are available in source and missing in target.
source_only = []
added = dict1_keys - dict2_keys
for item in added:
source_only.append({'schema_name': item,
'scid': src_schema_dict[item]})
target_only = []
# Keys that are available in target and missing in source.
removed = dict2_keys - dict1_keys
for item in removed:
target_only.append({'schema_name': item,
'scid': tar_schema_dict[item]})
in_both_database = []
for item in intersect_keys:
in_both_database.append({'schema_name': item,
'src_scid': src_schema_dict[item],
'tar_scid': tar_schema_dict[item]})
schema_result = {'source_only': source_only, 'target_only': target_only,
'in_both_database': in_both_database}
return schema_result

View File

@ -9,20 +9,17 @@
"""Schema diff object comparison."""
import copy
from flask import render_template
from flask_babelex import gettext
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
from pgadmin.tools.schema_diff.model import SchemaDiffModel
class SchemaDiffObjectCompare:
keys_to_ignore = ['oid', 'schema']
keys_to_ignore = ['oid', 'oid-2']
@staticmethod
def get_schema(sid, did, scid):
@ -57,28 +54,28 @@ class SchemaDiffObjectCompare:
:param kwargs:
:return:
"""
source_params = {'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'),
'scid': kwargs.get('source_scid')
}
'did': kwargs.get('source_did')}
target_params = {'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid')
}
'did': kwargs.get('target_did')}
group_name = kwargs.get('group_name')
ignore_whitespaces = kwargs.get('ignore_whitespaces')
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)
source = {}
target = {}
source = self.fetch_objects_to_compare(**source_params)
if group_name == 'Database Objects':
source = self.fetch_objects_to_compare(**source_params)
target = self.fetch_objects_to_compare(**target_params)
else:
source_params['scid'] = kwargs.get('source_scid')
target_params['scid'] = kwargs.get('target_scid')
target = self.fetch_objects_to_compare(**target_params)
if 'scid' in source_params and source_params['scid'] is not None:
source = self.fetch_objects_to_compare(**source_params)
if 'scid' in target_params and target_params['scid'] is not None:
target = self.fetch_objects_to_compare(**target_params)
# If both the dict have no items then return None.
if not (source or target) or (
@ -88,11 +85,11 @@ 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,
node_label=self.blueprint.collection_label,
group_name=group_name,
ignore_whitespaces=ignore_whitespaces,
ignore_keys=self.keys_to_ignore)
@ -105,17 +102,23 @@ class SchemaDiffObjectCompare:
source_params = {'gid': 1,
'sid': kwargs.get('source_sid'),
'did': kwargs.get('source_did'),
'scid': kwargs.get('source_scid'),
'oid': kwargs.get('source_oid')
}
target_params = {'gid': 1,
'sid': kwargs.get('target_sid'),
'did': kwargs.get('target_did'),
'scid': kwargs.get('target_scid'),
'oid': kwargs.get('target_oid')
}
source_scid = kwargs.get('source_scid')
if source_scid is not None and source_scid != 0:
source_params['scid'] = source_scid
target_scid = kwargs.get('target_scid')
if target_scid is not None and target_scid != 0:
target_params['scid'] = target_scid
source = self.get_sql_from_diff(**source_params)
target = self.get_sql_from_diff(**target_params)

View File

@ -16,7 +16,8 @@ from pgadmin.tools.schema_diff.model import SchemaDiffModel
count = 1
list_keys_array = ['name', 'colname', 'argid', 'token', 'option', 'conname',
'member_name', 'label', 'attname']
'member_name', 'label', 'attname', 'fdwoption',
'fsrvoption', 'umoption']
def compare_dictionaries(**kwargs):
@ -29,7 +30,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')
node = kwargs.get('node')
@ -50,6 +51,7 @@ def compare_dictionaries(**kwargs):
# Keys that are available in source and missing in target.
source_only = []
source_dependencies = []
added = dict1_keys - dict2_keys
global count
for item in added:
@ -63,18 +65,25 @@ def compare_dictionaries(**kwargs):
temp_src_params['json_resp'] = False
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
temp_src_params.update({
'diff_schema': target_schema
})
diff_ddl = view_object.get_sql_from_table_diff(**temp_src_params)
source_dependencies = \
view_object.get_table_submodules_dependencies(
**temp_src_params)
else:
temp_src_params = copy.deepcopy(source_params)
temp_src_params['oid'] = source_object_id
# Provide Foreign Data Wrapper ID
if 'fdwid' in source_dict[item]:
temp_src_params['fdwid'] = source_dict[item]['fdwid']
# Provide Foreign Server ID
if 'fsid' in source_dict[item]:
temp_src_params['fsid'] = source_dict[item]['fsid']
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
temp_src_params.update({
'diff_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,
show_system_objects=None, is_schema_diff=True)
source_only.append({
'id': count,
@ -85,7 +94,9 @@ def compare_dictionaries(**kwargs):
'status': SchemaDiffModel.COMPARISON_STATUS['source_only'],
'source_ddl': source_ddl,
'target_ddl': '',
'diff_ddl': diff_ddl
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': source_dependencies
})
count += 1
@ -110,6 +121,13 @@ def compare_dictionaries(**kwargs):
else:
temp_tgt_params = copy.deepcopy(target_params)
temp_tgt_params['oid'] = target_object_id
# Provide Foreign Data Wrapper ID
if 'fdwid' in target_dict[item]:
temp_tgt_params['fdwid'] = target_dict[item]['fdwid']
# Provide Foreign Server ID
if 'fsid' in target_dict[item]:
temp_tgt_params['fsid'] = target_dict[item]['fsid']
target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
temp_tgt_params.update(
{'drop_sql': True})
@ -124,13 +142,16 @@ def compare_dictionaries(**kwargs):
'status': SchemaDiffModel.COMPARISON_STATUS['target_only'],
'source_ddl': '',
'target_ddl': target_ddl,
'diff_ddl': diff_ddl
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': []
})
count += 1
# Compare the values of duplicates keys.
identical = []
different = []
diff_dependencies = []
for key in intersect_keys:
source_object_id = None
target_object_id = None
@ -149,7 +170,13 @@ def compare_dictionaries(**kwargs):
'oid': source_object_id,
'source_oid': source_object_id,
'target_oid': target_object_id,
'status': SchemaDiffModel.COMPARISON_STATUS['identical']
'status': SchemaDiffModel.COMPARISON_STATUS['identical'],
'group_name': group_name,
'dependencies': [],
'source_scid': source_params['scid']
if 'scid' in source_params else 0,
'target_scid': target_params['scid']
if 'scid' in target_params else 0,
})
else:
if node == 'table':
@ -174,12 +201,14 @@ def compare_dictionaries(**kwargs):
source_ddl = \
view_object.get_sql_from_table_diff(**temp_src_params)
diff_dependencies = \
view_object.get_table_submodules_dependencies(
**temp_src_params)
target_ddl = \
view_object.get_sql_from_table_diff(**temp_tgt_params)
diff_ddl = view_object.get_sql_from_submodule_diff(
source_params=temp_src_params,
target_params=temp_tgt_params,
target_schema=target_schema,
source=dict1[key], target=dict2[key], diff_dict=diff_dict,
ignore_whitespaces=ignore_whitespaces)
else:
@ -193,7 +222,19 @@ def compare_dictionaries(**kwargs):
temp_src_params['oid'] = source_object_id
temp_tgt_params['oid'] = target_object_id
# Provide Foreign Data Wrapper ID
if 'fdwid' in source_dict[key]:
temp_src_params['fdwid'] = source_dict[key]['fdwid']
temp_tgt_params['fdwid'] = target_dict[key]['fdwid']
# Provide Foreign Server ID
if 'fsid' in source_dict[key]:
temp_src_params['fsid'] = source_dict[key]['fsid']
temp_tgt_params['fsid'] = target_dict[key]['fsid']
source_ddl = view_object.get_sql_from_diff(**temp_src_params)
diff_dependencies = view_object.get_dependencies(
view_object.conn, source_object_id, where=None,
show_system_objects=None, is_schema_diff=True)
target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)
temp_tgt_params.update(
{'data': diff_dict})
@ -210,7 +251,9 @@ def compare_dictionaries(**kwargs):
'status': SchemaDiffModel.COMPARISON_STATUS['different'],
'source_ddl': source_ddl,
'target_ddl': target_ddl,
'diff_ddl': diff_ddl
'diff_ddl': diff_ddl,
'group_name': group_name,
'dependencies': diff_dependencies
})
count += 1
@ -498,13 +541,13 @@ def sort_list(source, target):
:return:
"""
# Check the above keys are exist in the dictionary
if len(source) > 0 and type(source[0]) == dict:
if source is not None and len(source) > 0 and type(source[0]) == dict:
tmp_key = is_key_exists(list_keys_array, source[0])
if tmp_key is not None:
source = sorted(source, key=lambda k: k[tmp_key])
# Check the above keys are exist in the dictionary
if len(target) > 0 and type(target[0]) == dict:
if target is not None and len(target) > 0 and type(target[0]) == dict:
tmp_key = is_key_exists(list_keys_array, target[0])
if tmp_key is not None:
target = sorted(target, key=lambda k: k[tmp_key])

View File

@ -162,3 +162,7 @@
.slick-cell .ml-2 {
margin-left: 2rem !important;
}
.slick-cell .ml-3 {
margin-left: 3rem !important;
}

View File

@ -105,7 +105,7 @@ let SchemaDiffSelect2Control =
controlsClassName: 'pgadmin-controls pg-el-sm-11 pg-el-12',
}),
className: function() {
return 'pgadmin-controls pg-el-sm-4';
return 'pgadmin-controls pg-el-sm-6';
},
events: {
'focus select': 'clearInvalid',

View File

@ -39,10 +39,8 @@ 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,
@ -109,7 +107,6 @@ export default class SchemaDiffUI {
}
raise_error_on_fail(alert_title, xhr) {
try {
var err = JSON.parse(xhr.responseText);
@ -146,11 +143,9 @@ export default class SchemaDiffUI {
url_params = self.model.toJSON();
if (url_params['source_sid'] == '' || _.isUndefined(url_params['source_sid']) ||
url_params['source_did'] == '' || _.isUndefined(url_params['source_did']) ||
url_params['source_scid'] == '' || _.isUndefined(url_params['source_scid']) ||
url_params['target_sid'] == '' || _.isUndefined(url_params['target_sid']) ||
url_params['target_did'] == '' || _.isUndefined(url_params['target_did']) ||
url_params['target_scid'] == '' || _.isUndefined(url_params['target_scid'])
url_params['source_did'] == '' || _.isUndefined(url_params['source_did']) ||
url_params['target_sid'] == '' || _.isUndefined(url_params['target_sid']) ||
url_params['target_did'] == '' || _.isUndefined(url_params['target_did'])
) {
Alertify.alert(gettext('Selection Error'), gettext('Please select source and target.'));
return false;
@ -289,18 +284,18 @@ 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 '<i class="ml-2 wcTabIcon '+ icon +'"></i><span>' + value + '</span>';
return '<i class="ml-3 wcTabIcon '+ icon +'"></i><span>' + value + '</span>';
};
// Grid Columns
var grid_width = (self.grid_width - 47) / 2 ;
var columns = [
checkboxSelector.getColumnDefinition(),
{id: 'title', name: gettext('Schema Objects'), field: 'title', minWidth: grid_width, formatter: formatColumnTitle},
{id: 'title', name: gettext('Objects'), field: 'title', minWidth: grid_width, formatter: formatColumnTitle},
{id: 'status', name: gettext('Comparison Result'), field: 'status', minWidth: grid_width},
{id: 'label', name: gettext('Schema Objects'), field: 'label', width: 0, minWidth: 0, maxWidth: 0,
{id: 'label', name: gettext('Objects'), field: 'label', width: 0, minWidth: 0, maxWidth: 0,
cssClass: 'really-hidden', headerCssClass: 'really-hidden'},
{id: 'type', name: gettext('Schema Objects'), field: 'type', width: 0, minWidth: 0, maxWidth: 0,
{id: 'type', name: gettext('Objects'), field: 'type', width: 0, minWidth: 0, maxWidth: 0,
cssClass: 'really-hidden', headerCssClass: 'really-hidden'},
{id: 'id', name: 'id', field: 'id', width: 0, minWidth: 0, maxWidth: 0,
cssClass: 'really-hidden', headerCssClass: 'really-hidden' },
@ -316,7 +311,18 @@ export default class SchemaDiffUI {
// Grouping by Schema Object
self.groupBySchemaObject = function() {
self.dataView.setGrouping({
self.dataView.setGrouping([{
getter: 'group_name',
formatter: function (g) {
let icon = 'icon-schema';
if (g.rows[0].group_name == 'Database Objects'){
icon = 'icon-coll-database';
}
return '<i class="wcTabIcon '+ icon +'"></i><span>' + g.rows[0].group_name;
},
aggregateCollapsed: true,
lazyTotalsCalculation: true,
}, {
getter: 'type',
formatter: function (g) {
let icon = 'icon-coll-' + g.value;
@ -330,8 +336,9 @@ export default class SchemaDiffUI {
return '<i class="wcTabIcon '+ icon +'"></i><span>' + g.rows[0].label + ' - ' + gettext('Identical') + ': <strong>' + identical + '</strong>&nbsp;&nbsp;' + gettext('Different') + ': <strong>' + different + '</strong>&nbsp;&nbsp;' + gettext('Source Only') + ': <strong>' + source_only + '</strong>&nbsp;&nbsp;' + gettext('Target Only') + ': <strong>' + target_only + '</strong></span>';
},
aggregateCollapsed: true,
collapsed: true,
lazyTotalsCalculation: true,
});
}]);
};
var groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider({ checkboxSelect: true,
@ -503,6 +510,8 @@ export default class SchemaDiffUI {
target_oid = data.target_oid;
url_params['trans_id'] = self.trans_id;
url_params['source_scid'] = data.source_scid;
url_params['target_scid'] = data.target_scid;
url_params['source_oid'] = source_oid;
url_params['target_oid'] = target_oid;
url_params['comp_status'] = data.status;
@ -607,37 +616,6 @@ 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) {
let self_local = this;
if (!_.isUndefined(m.get('source_did')) && !_.isNull(m.get('source_did'))
&& m.get('source_did') !== '') {
setTimeout(function() {
if (self_local.options.length > 0) {
m.set('source_scid', self_local.options[0].value);
}
}, 10);
return false;
}
setTimeout(function() {
m.set('source_scid', undefined);
}, 10);
return true;
},
}, {
name: 'target_sid', label: false,
control: SchemaDiffSelect2Control,
@ -698,37 +676,6 @@ 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) {
let self_local = this;
if (!_.isUndefined(m.get('target_did')) && !_.isNull(m.get('target_did'))
&& m.get('target_did') !== '') {
setTimeout(function() {
if (self_local.options.length > 0) {
m.set('target_scid', self_local.options[0].value);
}
}, 10);
return false;
}
setTimeout(function() {
m.set('target_scid', undefined);
}, 10);
return true;
},
}],
});
@ -760,7 +707,7 @@ 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(`<div class='obj_properties container-fluid'>
<div class='pg-panel-message'>` + gettext('Select the server, database and schema for the source and target and click <strong>Compare</strong> to compare them.') + '</div></div>');
<div class='pg-panel-message'>` + gettext('Select the server and database for the source and target and click <strong>Compare</strong> to compare them.') + '</div></div>');
self.grid_width = $('#schema-diff-grid').width();
self.grid_height = this.panel_obj.height();

View File

@ -23,7 +23,7 @@ from pgadmin.utils.versioned_template_loader import \
get_version_mapping_directories
class SchemaDiffTestCase(BaseTestGenerator):
class SchemaDiffTestCase():
""" This class will test the schema diff. """
scenarios = [
# Fetching default URL for database node.