From 6713bc068bd53ee7b95f27c21abf626b6fd70656 Mon Sep 17 00:00:00 2001 From: Nikhil Mohite Date: Tue, 30 Jun 2020 17:06:55 +0530 Subject: [PATCH] Fixed SonarQube code smells 'Refactor this function to reduce its Cognitive Complexity'. --- .../databases/schemas/functions/__init__.py | 642 ++++++++++-------- .../databases/schemas/sequences/__init__.py | 194 +++--- 2 files changed, 449 insertions(+), 387 deletions(-) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py index af21b598b..9dbc903dd 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py @@ -10,30 +10,30 @@ """Implements Functions/Procedures Node.""" import copy -import simplejson as json import re import sys import traceback from functools import wraps -import pgadmin.browser.server_groups.servers.databases as databases -from flask import render_template, make_response, request, jsonify, \ +import simplejson as json +from flask import render_template, request, jsonify, \ current_app from flask_babelex import gettext + +import pgadmin.browser.server_groups.servers.databases as databases +from config import PG_DEFAULT_DRIVER from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ - SchemaChildModule, DataTypeReader, get_schema + SchemaChildModule, DataTypeReader from pgadmin.browser.server_groups.servers.databases.utils import \ parse_sec_labels_from_db, parse_variables_from_db from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \ parse_priv_to_db from pgadmin.browser.utils import PGChildNodeView +from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare +from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry 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.model import SchemaDiffModel -from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare class FunctionModule(SchemaChildModule): @@ -247,6 +247,47 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): 'probin' ] + @staticmethod + def _create_wrap_data(req, key, data): + list_params = [] + if request.method == 'GET': + list_params = ['arguments', 'variables', 'proacl', + 'seclabels', 'acl', 'args'] + + if ( + key in list_params and req[key] != '' and + req[key] is not None + ): + # Coverts string into python list as expected. + data[key] = json.loads(req[key], encoding='utf-8') + elif ( + key == 'proretset' or key == 'proisstrict' or + key == 'prosecdef' or key == 'proiswindow' or + key == 'proleakproof' + ): + if req[key] == 'true' or req[key] is True: + data[key] = True + else: + data[key] = False if ( + req[key] == 'false' or req[key] is False) else '' + else: + data[key] = req[key] + + @staticmethod + def _remove_parameters_for_c_lang(req, req_args): + # We need to remove 'prosrc' from the required arguments list + # if language is 'c'. + if req['lanname'] == 'c' and 'prosrc' in req_args: + req_args.remove('prosrc') + + @staticmethod + def _get_request_data(): + if request.data: + req = json.loads(request.data, encoding='utf-8') + else: + req = request.args or request.form + return req + def validate_request(f): """ Works as a decorator. @@ -255,20 +296,11 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): @wraps(f) def wrap(self, **kwargs): - - data = {} - if request.data: - req = json.loads(request.data, encoding='utf-8') - else: - req = request.args or request.form + req = FunctionView._get_request_data() if 'fnid' not in kwargs: req_args = self.required_args - # We need to remove 'prosrc' from the required arguments list - # if language is 'c'. - if req['lanname'] == 'c' and 'prosrc' in req_args: - req_args.remove('prosrc') - + FunctionView._remove_parameters_for_c_lang(req, req_args) for arg in req_args: if (arg not in req or req[arg] == '') or \ (arg == 'probin' and req['lanname'] == 'c' and @@ -281,29 +313,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ).format(arg) ) - list_params = [] - if request.method == 'GET': - list_params = ['arguments', 'variables', 'proacl', - 'seclabels', 'acl', 'args'] - + data = {} for key in req: - if ( - key in list_params and req[key] != '' and - req[key] is not None - ): - # Coverts string into python list as expected. - data[key] = json.loads(req[key], encoding='utf-8') - elif ( - key == 'proretset' or key == 'proisstrict' or - key == 'prosecdef' or key == 'proiswindow' or - key == 'proleakproof' - ): - data[key] = True if ( - req[key] == 'true' or req[key] is True) \ - else False if (req[key] == 'false' or - req[key] is False) else '' - else: - data[key] = req[key] + FunctionView._create_wrap_data(req, key, data) self.request = data return f(self, **kwargs) @@ -360,9 +372,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): scid: Schema Id """ - SQL = render_template("/".join([self.sql_template_path, 'node.sql']), + sql = render_template("/".join([self.sql_template_path, 'node.sql']), scid=scid) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) @@ -384,12 +396,12 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): """ res = [] - SQL = render_template( + sql = render_template( "/".join([self.sql_template_path, 'node.sql']), scid=scid, fnid=fnid ) - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=rset) @@ -481,7 +493,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): # default values. if len(proargmodes_fltrd) > len(proargdefaultvals): dif = len(proargmodes_fltrd) - len(proargdefaultvals) - while (dif > 0): + while dif > 0: proargdefaultvals.insert(0, '') dif -= 1 @@ -523,10 +535,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ] Where Arguments: - proargtypes: Argument Types (Data Type) - proargmodes: Argument Modes [IN, OUT, INOUT, VARIADIC] - proargnames: Argument Name - proargdefaultvals: Default Value of the Argument + # proargtypes: Argument Types (Data Type) + # proargmodes: Argument Modes [IN, OUT, INOUT, VARIADIC] + # proargnames: Argument Name + # proargdefaultvals: Default Value of the Argument """ arguments = self._get_argument_values(data) proargtypes = arguments['proargtypes'] @@ -570,10 +582,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): cnt = 0 for m in proargmodes: if m == 'o': # Out Mode - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'get_out_types.sql']), out_arg_oid=proallargtypes[cnt]) - status, out_arg_type = self.conn.execute_scalar(SQL) + status, out_arg_type = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=out_arg_type) @@ -724,10 +736,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): res = [{'label': '', 'value': ''}] try: - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'get_languages.sql']) ) - status, rows = self.conn.execute_dict(SQL) + status, rows = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) @@ -765,10 +777,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): This function will return list of variables available for table spaces. """ - SQL = render_template( + sql = render_template( "/".join([self.sql_template_path, 'variables.sql']) ) - status, rset = self.conn.execute_dict(SQL) + status, rset = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=rset) @@ -789,7 +801,6 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): sid: Server Id did: Database Id scid: Schema Id - fnid: Function Id Returns: Function object in json format. @@ -856,10 +867,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): try: for fnid in data['ids']: # Fetch Name and Schema Name to delete the Function. - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'delete.sql']), scid=scid, fnid=fnid) - status, res = self.conn.execute_2darray(SQL) + status, res = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) elif not res['rows']: @@ -873,15 +884,15 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ) ) - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'delete.sql']), name=res['rows'][0]['name'], func_args=res['rows'][0]['func_args'], nspname=res['rows'][0]['nspname'], cascade=cascade) if only_sql: - return SQL - status, res = self.conn.execute_scalar(SQL) + return sql + status, res = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=res) @@ -907,14 +918,14 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): fnid: Function Id """ - status, SQL = self._get_sql(gid, sid, did, scid, self.request, fnid) + status, sql = self._get_sql(gid, sid, did, scid, self.request, fnid) if not status: - return internal_server_error(errormsg=SQL) + return internal_server_error(errormsg=sql) - if SQL and SQL.strip('\n') and SQL.strip(' '): + if sql and sql.strip('\n') and sql.strip(' '): - status, res = self.conn.execute_scalar(SQL) + status, res = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=res) @@ -954,6 +965,51 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): } ) + @staticmethod + def _check_argtype(args, args_without_name, a): + if 'argtype' in a: + args += a['argtype'] + args_without_name.append(a['argtype']) + + def _get_arguments(self, args_list, args, args_without_name): + cnt = 1 + for a in args_list: + if ( + ( + 'argmode' in a and a['argmode'] != 'OUT' and + a['argmode'] is not None + ) or 'argmode' not in a + ): + if 'argmode' in a: + args += a['argmode'] + " " + if ( + 'argname' in a and a['argname'] != '' and + a['argname'] is not None + ): + args += self.qtIdent( + self.conn, a['argname']) + " " + + FunctionView._check_argtype(args, args_without_name, a) + + if cnt < len(args_list): + args += ', ' + cnt += 1 + + def _parse_privilege_data(self, resp_data): + # Parse privilege data + if 'acl' in resp_data: + resp_data['acl'] = parse_priv_to_db(resp_data['acl'], ['X']) + + # Check Revoke all for public + resp_data['revoke_all'] = self._set_revoke_all( + resp_data['acl']) + + def _get_schema_name_from_iod(self, resp_data): + # Get Schema Name from its OID. + if 'pronamespace' in resp_data: + resp_data['pronamespace'] = self._get_schema( + resp_data['pronamespace']) + @check_precondition def sql(self, gid, sid, did, scid, fnid=None, diff_schema=None, json_resp=True): @@ -975,7 +1031,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): # Fetch the function definition. args = u'' args_without_name = [] - cnt = 1 + args_list = [] vol_dict = {'v': 'VOLATILE', 's': 'STABLE', 'i': 'IMMUTABLE'} @@ -983,27 +1039,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): args_list = resp_data['arguments'] resp_data['args'] = resp_data['arguments'] - for a in args_list: - if ( - ( - 'argmode' in a and a['argmode'] != 'OUT' and - a['argmode'] is not None - ) or 'argmode' not in a - ): - if 'argmode' in a: - args += a['argmode'] + " " - if ( - 'argname' in a and a['argname'] != '' and - a['argname'] is not None - ): - args += self.qtIdent( - self.conn, a['argname']) + " " - if 'argtype' in a: - args += a['argtype'] - args_without_name.append(a['argtype']) - if cnt < len(args_list): - args += ', ' - cnt += 1 + self._get_arguments(args_list, args, args_without_name) resp_data['func_args'] = args.strip(' ') @@ -1019,20 +1055,17 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ) # Get Schema Name from its OID. - if 'pronamespace' in resp_data: - resp_data['pronamespace'] = self._get_schema( - resp_data['pronamespace']) + self._get_schema_name_from_iod(resp_data) - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'get_definition.sql'] ), data=resp_data, fnid=fnid, scid=scid) - status, res = self.conn.execute_2darray(SQL) + status, res = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) - - if diff_schema: + elif diff_schema: res['rows'][0]['nspname'] = diff_schema # Add newline and tab before each argument to format @@ -1044,12 +1077,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): replace(', ', ',\n\t') + ')' # Parse privilege data - if 'acl' in resp_data: - resp_data['acl'] = parse_priv_to_db(resp_data['acl'], ['X']) - - # Check Revoke all for public - resp_data['revoke_all'] = self._set_revoke_all( - resp_data['acl']) + self._parse_privilege_data(resp_data) # Generate sql for "SQL panel" # func_def is procedure signature with default arguments @@ -1063,29 +1091,20 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): object_type = 'function' # Get Schema Name from its OID. - if 'pronamespace' in resp_data: - resp_data['pronamespace'] = self._get_schema( - resp_data['pronamespace'] - ) + self._get_schema_name_from_iod(resp_data) # Parse privilege data - if 'acl' in resp_data: - resp_data['acl'] = parse_priv_to_db(resp_data['acl'], ['X']) + self._parse_privilege_data(resp_data) - # Check Revoke all for public - resp_data['revoke_all'] = self._set_revoke_all( - resp_data['acl']) - - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'get_definition.sql'] ), data=resp_data, fnid=fnid, scid=scid) - status, res = self.conn.execute_2darray(SQL) + status, res = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) - - if diff_schema: + elif diff_schema: res['rows'][0]['nspname'] = diff_schema resp_data['pronamespace'] = diff_schema @@ -1118,10 +1137,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if not json_resp: return re.sub('\n{2,}', '\n\n', func_def) - SQL = sql_header + func_def - SQL = re.sub('\n{2,}', '\n\n', SQL) + sql = sql_header + func_def + sql = re.sub('\n{2,}', '\n\n', sql) - return ajax_response(response=SQL) + return ajax_response(response=sql) @check_precondition @validate_request @@ -1139,17 +1158,188 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): SQL statements to create/update the Domain. """ - status, SQL = self._get_sql(gid, sid, did, scid, self.request, fnid) + status, sql = self._get_sql(gid, sid, did, scid, self.request, fnid) if status: - SQL = re.sub('\n{2,}', '\n\n', SQL) + sql = re.sub('\n{2,}', '\n\n', sql) return make_json_response( - data=SQL, + data=sql, status=200 ) else: - SQL = re.sub('\n{2,}', '\n\n', SQL) - return SQL + sql = re.sub('\n{2,}', '\n\n', sql) + return sql + + @staticmethod + def _update_arguments_for_get_sql(data, old_data): + # If Function Definition/Arguments are changed then merge old + # Arguments with changed ones for Create/Replace Function + # SQL statement + if 'arguments' in data and len(data['arguments']) > 0: + for arg in data['arguments']['changed']: + for old_arg in old_data['arguments']: + if arg['argid'] == old_arg['argid']: + old_arg.update(arg) + break + data['arguments'] = old_data['arguments'] + elif data['change_func']: + data['arguments'] = old_data['arguments'] + + @staticmethod + def _delete_variable_in_edit_mode(data, del_variables): + if 'variables' in data and 'deleted' in data['variables']: + for v in data['variables']['deleted']: + del_variables[v['name']] = v['value'] + + @staticmethod + def _prepare_final_dict(data, old_data, chngd_variables, del_variables, + all_ids_dict): + # To compare old and new variables, preparing name : + # value dict + + # if 'variables' in data and 'changed' in data['variables']: + # for v in data['variables']['changed']: + # chngd_variables[v['name']] = v['value'] + # + # if 'variables' in data and 'added' in data['variables']: + # for v in data['variables']['added']: + # chngd_variables[v['name']] = v['value'] + + # In case of schema diff we don't want variables from + # old data + if not all_ids_dict['is_schema_diff']: + for v in old_data['variables']: + old_data['chngd_variables'][v['name']] = v['value'] + + # Prepare final dict of new and old variables + for name, val in old_data['chngd_variables'].items(): + if ( + name not in chngd_variables and + name not in del_variables + ): + chngd_variables[name] = val + + # Prepare dict in [{'name': var_name, 'value': var_val},..] + # format + for name, val in chngd_variables.items(): + data['merged_variables'].append({'name': name, + 'value': val}) + + @staticmethod + def _parser_privilege(data): + if 'acl' in data: + for key in ['added', 'deleted', 'changed']: + if key in data['acl']: + data['acl'][key] = parse_priv_to_db( + data['acl'][key], ["X"]) + + @staticmethod + def _merge_variable_changes(data, chngd_variables): + if 'variables' in data and 'changed' in data['variables']: + for v in data['variables']['changed']: + chngd_variables[v['name']] = v['value'] + + if 'variables' in data and 'added' in data['variables']: + for v in data['variables']['added']: + chngd_variables[v['name']] = v['value'] + + @staticmethod + def _merge_variables(data): + if 'variables' in data and 'changed' in data['variables']: + for v in data['variables']['changed']: + data['merged_variables'].append(v) + + if 'variables' in data and 'added' in data['variables']: + for v in data['variables']['added']: + data['merged_variables'].append(v) + + def _get_sql_for_edit_mode(self, data, parallel_dict, all_ids_dict, + vol_dict): + if 'proparallel' in data and data['proparallel']: + data['proparallel'] = parallel_dict[data['proparallel']] + + # Fetch Old Data from database. + old_data = self._fetch_properties(all_ids_dict['gid'], + all_ids_dict['sid'], + all_ids_dict['did'], + all_ids_dict['scid'], + all_ids_dict['fnid']) + # Most probably this is due to error + if not isinstance(old_data, dict): + return False, gettext( + "Could not find the function in the database." + ) + + # Get Schema Name + old_data['pronamespace'] = self._get_schema( + old_data['pronamespace'] + ) + + if 'provolatile' in old_data and \ + old_data['provolatile'] is not None: + old_data['provolatile'] = vol_dict[old_data['provolatile']] + + if 'proparallel' in old_data and \ + old_data['proparallel'] is not None: + old_data['proparallel'] = \ + parallel_dict[old_data['proparallel']] + + # If any of the below argument is changed, + # then CREATE OR REPLACE SQL statement should be called + fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c', + 'provolatile', 'proisstrict', 'prosecdef', + 'proparallel', 'procost', 'proleakproof', + 'arguments', 'prorows', 'prosupportfunc'] + + data['change_func'] = False + for arg in fun_change_args: + if (arg == 'arguments' and arg in data and len( + data[arg]) > 0) or arg in data: + data['change_func'] = True + + # If Function Definition/Arguments are changed then merge old + # Arguments with changed ones for Create/Replace Function + # SQL statement + FunctionView._update_arguments_for_get_sql(data, old_data) + + # Parse Privileges + FunctionView._parser_privilege(data) + + # Parse Variables + chngd_variables = {} + data['merged_variables'] = [] + old_data['chngd_variables'] = {} + del_variables = {} + + # If Function Definition/Arguments are changed then, + # Merge old, new (added, changed, deleted) variables, + # which will be used in the CREATE or REPLACE Function sql + # statement + if data['change_func']: + # Deleted Variables + FunctionView._delete_variable_in_edit_mode(data, del_variables) + FunctionView._merge_variable_changes(data, chngd_variables) + # if 'variables' in data and 'changed' in data['variables']: + # for v in data['variables']['changed']: + # chngd_variables[v['name']] = v['value'] + # + # if 'variables' in data and 'added' in data['variables']: + # for v in data['variables']['added']: + # chngd_variables[v['name']] = v['value'] + + # Prepare final dict + FunctionView._prepare_final_dict(data, old_data, chngd_variables, + del_variables, all_ids_dict) + else: + FunctionView._merge_variables(data) + + self.reformat_prosrc_code(data) + + sql = render_template( + "/".join([self.sql_template_path, 'update.sql']), + data=data, o_data=old_data + ) + return sql def _get_sql(self, gid, sid, did, scid, data, fnid=None, is_sql=False, is_schema_diff=False): @@ -1163,16 +1353,16 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): scid: Schema Id data: Function data fnid: Function Id + is_sql: sql flag + is_schema_diff: schema diff flag """ vol_dict = {'v': 'VOLATILE', 's': 'STABLE', 'i': 'IMMUTABLE'} parallel_dict = {'u': 'UNSAFE', 's': 'SAFE', 'r': 'RESTRICTED'} # Get Schema Name from its OID. - if 'pronamespace' in data: - data['pronamespace'] = self._get_schema( - data['pronamespace'] - ) + self._get_schema_name_from_iod(data) + if 'provolatile' in data: data['provolatile'] = vol_dict[data['provolatile']]\ if data['provolatile'] else '' @@ -1180,162 +1370,32 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if fnid is not None: # Edit Mode - if 'proparallel' in data and data['proparallel']: - data['proparallel'] = parallel_dict[data['proparallel']] - - # Fetch Old Data from database. - old_data = self._fetch_properties(gid, sid, did, scid, fnid) - # Most probably this is due to error - if not isinstance(old_data, dict): - return False, gettext( - "Could not find the function in the database." - ) - - # Get Schema Name - old_data['pronamespace'] = self._get_schema( - old_data['pronamespace'] - ) - - if 'provolatile' in old_data and \ - old_data['provolatile'] is not None: - old_data['provolatile'] = vol_dict[old_data['provolatile']] - - if 'proparallel' in old_data and \ - old_data['proparallel'] is not None: - old_data['proparallel'] = \ - parallel_dict[old_data['proparallel']] - - # If any of the below argument is changed, - # then CREATE OR REPLACE SQL statement should be called - fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c', - 'provolatile', 'proisstrict', 'prosecdef', - 'proparallel', 'procost', 'proleakproof', - 'arguments', 'prorows', 'prosupportfunc'] - - data['change_func'] = False - for arg in fun_change_args: - if (arg == 'arguments' and arg in data and len(data[arg]) > 0)\ - or arg in data: - data['change_func'] = True - - # If Function Definition/Arguments are changed then merge old - # Arguments with changed ones for Create/Replace Function - # SQL statement - if 'arguments' in data and len(data['arguments']) > 0: - for arg in data['arguments']['changed']: - for old_arg in old_data['arguments']: - if arg['argid'] == old_arg['argid']: - old_arg.update(arg) - break - data['arguments'] = old_data['arguments'] - elif data['change_func']: - data['arguments'] = old_data['arguments'] - - # Parse Privileges - if 'acl' in data: - for key in ['added', 'deleted', 'changed']: - if key in data['acl']: - data['acl'][key] = parse_priv_to_db( - data['acl'][key], ["X"]) - - # Parse Variables - chngd_variables = {} - data['merged_variables'] = [] - old_data['chngd_variables'] = {} - del_variables = {} - - # If Function Definition/Arguments are changed then, - # Merge old, new (added, changed, deleted) variables, - # which will be used in the CREATE or REPLACE Function sql - # statement - - if data['change_func']: - # To compare old and new variables, preparing name : - # value dict - - # Deleted Variables - if 'variables' in data and 'deleted' in data['variables']: - for v in data['variables']['deleted']: - del_variables[v['name']] = v['value'] - - if 'variables' in data and 'changed' in data['variables']: - for v in data['variables']['changed']: - chngd_variables[v['name']] = v['value'] - - if 'variables' in data and 'added' in data['variables']: - for v in data['variables']['added']: - chngd_variables[v['name']] = v['value'] - - # In case of schema diff we don't want variables from - # old data - if not is_schema_diff: - for v in old_data['variables']: - old_data['chngd_variables'][v['name']] = v['value'] - - # Prepare final dict of new and old variables - for name, val in old_data['chngd_variables'].items(): - if ( - name not in chngd_variables and - name not in del_variables - ): - chngd_variables[name] = val - - # Prepare dict in [{'name': var_name, 'value': var_val},..] - # format - for name, val in chngd_variables.items(): - data['merged_variables'].append({'name': name, - 'value': val}) - else: - if 'variables' in data and 'changed' in data['variables']: - for v in data['variables']['changed']: - data['merged_variables'].append(v) - - if 'variables' in data and 'added' in data['variables']: - for v in data['variables']['added']: - data['merged_variables'].append(v) - - self.reformat_prosrc_code(data) - - SQL = render_template( - "/".join([self.sql_template_path, 'update.sql']), - data=data, o_data=old_data - ) + all_ids_dict = { + 'gid': gid, + 'sid': sid, + 'did': did, + 'scid': scid, + 'data': data, + 'fnid': fnid, + 'is_sql': is_sql, + 'is_schema_diff': is_schema_diff, + } + sql = self._get_sql_for_edit_mode(data, parallel_dict, + all_ids_dict, vol_dict) else: # Parse Privileges - if 'acl' in data: - data['acl'] = parse_priv_to_db(data['acl'], ["X"]) - - # Check Revoke all for public - data['revoke_all'] = self._set_revoke_all(data['acl']) + self._parse_privilege_data(data) args = u'' args_without_name = [] - cnt = 1 + args_list = [] if 'arguments' in data and len(data['arguments']) > 0: args_list = data['arguments'] elif 'args' in data and len(data['args']) > 0: args_list = data['args'] - for a in args_list: - if ( - ( - 'argmode' in a and a['argmode'] != 'OUT' and - a['argmode'] is not None - ) or 'argmode' not in a - ): - if 'argmode' in a: - args += a['argmode'] + " " - if ( - 'argname' in a and a['argname'] != '' and - a['argname'] is not None - ): - args += self.qtIdent(self.conn, a['argname']) + " " - if 'argtype' in a: - args += a['argtype'] - args_without_name.append(a['argtype']) - if cnt < len(args_list): - args += ', ' - cnt += 1 + + self._get_arguments(args_list, args, args_without_name) data['func_args'] = args.strip(' ') @@ -1344,10 +1404,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): self.reformat_prosrc_code(data) # Create mode - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'create.sql']), data=data, is_sql=is_sql) - return True, SQL.strip('\n') + return True, sql.strip('\n') def _fetch_properties(self, gid, sid, did, scid, fnid=None): """ @@ -1362,12 +1422,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): fnid: Function Id """ - resp_data = {} - - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'properties.sql']), scid=scid, fnid=fnid) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) @@ -1384,9 +1442,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): resp_data.update(frmtd_proargs) # Fetch privileges - SQL = render_template("/".join([self.sql_template_path, 'acl.sql']), + sql = render_template("/".join([self.sql_template_path, 'acl.sql']), fnid=fnid) - status, proaclres = self.conn.execute_dict(SQL) + status, proaclres = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) @@ -1431,10 +1489,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): Args: scid: Schema Id """ - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'get_schema.sql']), scid=scid) - status, schema_name = self.conn.execute_scalar(SQL) + status, schema_name = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=schema_name) @@ -1462,12 +1520,12 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): res = [{'label': '', 'value': ''}] try: - SQL = render_template( + sql = render_template( "/".join([self.sql_template_path, 'get_support_functions.sql']), show_system_objects=self.blueprint.show_system_objects ) - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) @@ -1495,7 +1553,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): sid: Server Id did: Database Id scid: Schema Id - doid: Function Id + fnid: Function Id """ dependents_result = self.get_dependents(self.conn, fnid) return ajax_response( @@ -1514,7 +1572,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): sid: Server Id did: Database Id scid: Schema Id - doid: Function Id + fnid: Function Id """ dependencies_result = self.get_dependencies(self.conn, fnid) return ajax_response( @@ -1532,7 +1590,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): sid: Server Id did: Database Id scid: Schema Id - doid: Function Id + fnid: Function Id """ # Fetch the function definition. sql = render_template("/".join([self.sql_template_path, @@ -1576,7 +1634,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): sid: Server Id did: Database Id scid: Schema Id - doid: Function Id + fnid: Function Id """ resp_data = self._fetch_properties(gid, sid, did, scid, fnid) # Most probably this is due to error @@ -1662,7 +1720,6 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): def get_sql_from_diff(self, gid, sid, did, scid, oid, data=None, diff_schema=None, drop_sql=False): - sql = '' if data: if diff_schema: data['schema'] = diff_schema @@ -1670,7 +1727,6 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): True) # Check if return type is changed then we need to drop the # function first and then recreate it. - drop_fun_sql = '' if 'prorettypename' in data: drop_fun_sql = self.delete(gid=gid, sid=sid, did=did, scid=scid, fnid=oid, only_sql=True) @@ -1734,9 +1790,9 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): return res if not oid: - SQL = render_template("/".join([self.sql_template_path, + sql = render_template("/".join([self.sql_template_path, 'node.sql']), scid=scid) - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py index e91e0ca39..f82a329f6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/__init__.py @@ -9,22 +9,24 @@ """Implements Sequence Node""" -import simplejson as json from functools import wraps -import pgadmin.browser.server_groups.servers.databases as database -from flask import render_template, make_response, request, jsonify + +import simplejson as json +from flask import render_template, request, jsonify from flask_babelex import gettext as _ + +import pgadmin.browser.server_groups.servers.databases as database +from config import PG_DEFAULT_DRIVER from pgadmin.browser.server_groups.servers.databases.schemas.utils \ import SchemaChildModule from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \ parse_priv_to_db from pgadmin.browser.utils import PGChildNodeView +from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare +from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry 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 SequenceModule(SchemaChildModule): @@ -292,16 +294,15 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): :return: """ - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid ) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return False, internal_server_error(errormsg=res) - - if len(res['rows']) == 0: + elif len(res['rows']) == 0: return False, gone( _("Could not find the sequence in the database.")) @@ -309,11 +310,11 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): res['rows'][0]['oid'] <= self.datlastsysoid) for row in res['rows']: - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'get_def.sql']), data=row ) - status, rset1 = self.conn.execute_dict(SQL) + status, rset1 = self.conn.execute_dict(sql) if not status: return False, internal_server_error(errormsg=rset1) @@ -325,22 +326,13 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): row['cache'] = rset1['rows'][0]['cache_value'] row['cycled'] = rset1['rows'][0]['is_cycled'] - sec_lbls = [] - if 'securities' in row and row['securities'] is not None: - for sec in row['securities']: - import re - sec = re.search(r'([^=]+)=(.*$)', sec) - sec_lbls.append({ - 'provider': sec.group(1), - 'label': sec.group(2) - }) - row['securities'] = sec_lbls + self._add_securities_to_row(row) - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'acl.sql']), scid=scid, seid=seid ) - status, dataclres = self.conn.execute_dict(SQL) + status, dataclres = self.conn.execute_dict(sql) if not status: return False, internal_server_error(errormsg=res) @@ -353,6 +345,18 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): return True, res['rows'][0] + def _add_securities_to_row(self, row): + sec_lbls = [] + if 'securities' in row and row['securities'] is not None: + for sec in row['securities']: + import re + sec = re.search(r'([^=]+)=(.*$)', sec) + sec_lbls.append({ + 'provider': sec.group(1), + 'label': sec.group(2) + }) + row['securities'] = sec_lbls + @check_precondition(action="create") def create(self, gid, sid, did, scid): """ @@ -389,14 +393,14 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): try: # The SQL below will execute CREATE DDL only - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'create.sql']), data=data, conn=self.conn ) except Exception as e: return internal_server_error(errormsg=e) - status, msg = self.conn.execute_scalar(SQL) + status, msg = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=msg) @@ -405,25 +409,25 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): # The SQL below will execute rest DMLs because we cannot execute # CREATE with any other - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn ) - SQL = SQL.strip('\n').strip(' ') - if SQL and SQL != "": - status, msg = self.conn.execute_scalar(SQL) + sql = sql.strip('\n').strip(' ') + if sql and sql != "": + status, msg = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=msg) # We need oid of newly created sequence. - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'get_oid.sql']), name=data['name'], schema=data['schema'] ) - SQL = SQL.strip('\n').strip(' ') + sql = sql.strip('\n').strip(' ') - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=rset) @@ -469,15 +473,15 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): try: for seid in data['ids']: - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid ) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) - if not res['rows']: + elif not res['rows']: return make_json_response( success=0, errormsg=_( @@ -488,15 +492,15 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): ) ) - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'delete.sql']), data=res['rows'][0], cascade=cascade ) if only_sql: - return SQL + return sql - status, res = self.conn.execute_scalar(SQL) + status, res = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=res) @@ -526,22 +530,22 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): data = request.form if request.form else json.loads( request.data, encoding='utf-8' ) - SQL, name = self.getSQL(gid, sid, did, data, scid, seid) + sql, name = self.get_SQL(gid, sid, did, data, scid, seid) # Most probably this is due to error - if not isinstance(SQL, str): - return SQL + if not isinstance(sql, str): + return sql - SQL = SQL.strip('\n').strip(' ') + sql = sql.strip('\n').strip(' ') - status, res = self.conn.execute_scalar(SQL) + status, res = self.conn.execute_scalar(sql) if not status: return internal_server_error(errormsg=res) - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'nodes.sql']), seid=seid ) - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=rset) row = rset['rows'][0] @@ -595,21 +599,21 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): "Could not find the required parameter ({})." ).format(arg) ) - SQL, name = self.getSQL(gid, sid, did, data, scid, seid) + sql, name = self.get_SQL(gid, sid, did, data, scid, seid) # Most probably this is due to error - if not isinstance(SQL, str): - return SQL + if not isinstance(sql, str): + return sql - SQL = SQL.strip('\n').strip(' ') - if SQL == '': - SQL = "--modified SQL" + sql = sql.strip('\n').strip(' ') + if sql == '': + sql = "--modified SQL" return make_json_response( - data=SQL, + data=sql, status=200 ) - def getSQL(self, gid, sid, did, data, scid, seid=None): + def get_SQL(self, gid, sid, did, data, scid, seid=None): """ This function will generate sql from model data. @@ -626,60 +630,63 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): ] if seid is not None: - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid ) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) - if len(res['rows']) == 0: + elif len(res['rows']) == 0: return gone(_("Could not find the sequence in the database.")) # Making copy of output for further processing old_data = dict(res['rows'][0]) old_data = self._formatter(old_data, scid, seid) - # To format privileges data coming from client - for key in ['relacl']: - if key in data and data[key] is not None: - if 'added' in data[key]: - data[key]['added'] = parse_priv_to_db( - data[key]['added'], self.acl - ) - if 'changed' in data[key]: - data[key]['changed'] = parse_priv_to_db( - data[key]['changed'], self.acl - ) - if 'deleted' in data[key]: - data[key]['deleted'] = parse_priv_to_db( - data[key]['deleted'], self.acl - ) + self._format_privilege_data(data) # If name is not present with in update data then copy it # from old data for arg in required_args: if arg not in data: data[arg] = old_data[arg] - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'update.sql']), data=data, o_data=old_data, conn=self.conn ) - return SQL, data['name'] if 'name' in data else old_data['name'] + return sql, data['name'] if 'name' in data else old_data['name'] else: # To format privileges coming from client if 'relacl' in data: data['relacl'] = parse_priv_to_db(data['relacl'], self.acl) - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'create.sql']), data=data, conn=self.conn ) - SQL += render_template( + sql += render_template( "/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn ) - return SQL, data['name'] + return sql, data['name'] + + def _format_privilege_data(self, data): + # To format privileges data coming from client + for key in ['relacl']: + if key in data and data[key] is not None: + if 'added' in data[key]: + data[key]['added'] = parse_priv_to_db( + data[key]['added'], self.acl + ) + if 'changed' in data[key]: + data[key]['changed'] = parse_priv_to_db( + data[key]['changed'], self.acl + ) + if 'deleted' in data[key]: + data[key]['deleted'] = parse_priv_to_db( + data[key]['deleted'], self.acl + ) @check_precondition(action="sql") def sql(self, gid, sid, did, scid, seid, diff_schema=None, @@ -697,22 +704,22 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): json_resp: json response or plain text response """ - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid ) - status, res = self.conn.execute_dict(SQL) + status, res = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=res) if len(res['rows']) == 0: return gone(_("Could not find the sequence in the database.")) for row in res['rows']: - SQL = render_template( + sql = render_template( "/".join([self.template_path, 'get_def.sql']), data=row ) - status, rset1 = self.conn.execute_dict(SQL) + status, rset1 = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=rset1) @@ -730,15 +737,15 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): result['schema'] = diff_schema result = self._formatter(result, scid, seid) - SQL, name = self.getSQL(gid, sid, did, result, scid) + sql, name = self.get_SQL(gid, sid, did, result, scid) # Most probably this is due to error - if not isinstance(SQL, str): - return SQL - SQL = SQL.strip('\n').strip(' ') + if not isinstance(sql, str): + return sql + sql = sql.strip('\n').strip(' ') # Return sql for schema diff if not json_resp: - return SQL + return sql sql_header = u"""-- SEQUENCE: {0}.{1}\n\n""".format( result['schema'], result['name']) @@ -747,9 +754,9 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): """.format(self.qtIdent(self.conn, result['schema'], result['name'])) - SQL = sql_header + SQL + sql = sql_header + sql - return ajax_response(response=SQL) + return ajax_response(response=sql) def _formatter(self, data, scid, seid): """ @@ -772,9 +779,9 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): data['securities'] = seclabels # We need to parse & convert ACL coming from database to json format - SQL = render_template("/".join([self.template_path, 'acl.sql']), + sql = render_template("/".join([self.template_path, 'acl.sql']), scid=scid, seid=seid) - status, acl = self.conn.execute_dict(SQL) + status, acl = self.conn.execute_dict(sql) if not status: return internal_server_error(errormsg=acl) @@ -919,9 +926,9 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): :return: """ res = dict() - SQL = render_template("/".join([self.template_path, + sql = render_template("/".join([self.template_path, 'nodes.sql']), scid=scid) - status, rset = self.conn.execute_2darray(SQL) + status, rset = self.conn.execute_2darray(sql) if not status: return internal_server_error(errormsg=res) @@ -946,11 +953,10 @@ class SequenceView(PGChildNodeView, SchemaDiffObjectCompare): :param drop_sql: True if need to drop the domains :return: """ - sql = '' if data: if diff_schema: data['schema'] = diff_schema - sql, name = self.getSQL(gid, sid, did, data, scid, oid) + sql, name = self.get_SQL(gid, sid, did, data, scid, oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did,