Fix a number of debugger execution issues. Fixes #3191

1. EPAS packages' function/procedure does not honour INOUT arguments, it converts INOUT to OUT.

2.  Packages' functions and procedures are not getting listed in their respected nodes in some scenarios like procedure having INOUT argument and function with void return type

3. The Reverse engineering SQL is not correct for Packages' functions/procedures

4. In case of INOUT argument, debugger asks for mendatory input which should not.

5. Re-executing a procedure whilst direct debugging doesn't work.
pull/14/head
Khushboo Vashi 2018-07-17 12:51:24 +01:00 committed by Dave Page
parent 39b41d7b24
commit 8869fd6eb3
36 changed files with 176 additions and 113 deletions

View File

@ -16,6 +16,7 @@ Features
Bug fixes
*********
| `Bug #3191 <https://redmine.postgresql.org/issues/3191>`_ - Fix a number of debugger execution issues.
| `Bug #3294 <https://redmine.postgresql.org/issues/3294>`_ - Infrastructure (and changes to the Query Tool) for realtime preference handling.
| `Bug #3309 <https://redmine.postgresql.org/issues/3309>`_ - Fix Directory format support for backups.
| `Bug #3316 <https://redmine.postgresql.org/issues/3316>`_ - Support running on systems without a system tray.

View File

@ -391,32 +391,13 @@ class EdbFuncView(PGChildNodeView, DataTypeReader):
proargmodenames = {'i': 'IN', 'o': 'OUT', 'b': 'INOUT',
'v': 'VARIADIC', 't': 'TABLE'}
# The proargtypes doesn't give OUT params, so we need to fetch
# those from database explicitly, below code is written for this
# purpose.
#
# proallargtypes gives all the Function's argument including OUT,
# but we have not used that column; as the data type of this
# column (i.e. oid[]) is not supported by oidvectortypes(oidvector)
# function which we have used to fetch the datatypes
# of the other parameters.
# EPAS explicitly converts OUT to INOUT, So we always have proargtypes
proargmodes_fltrd = copy.deepcopy(proargmodes)
proargnames_fltrd = []
cnt = 0
for m in proargmodes:
if m == 'o': # Out Mode
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)
if not status:
return internal_server_error(errormsg=out_arg_type)
# Insert out parameter datatype
proargtypes.insert(cnt, out_arg_type)
proargdefaultvals.insert(cnt, '')
elif m == 'v': # Variadic Mode
if m in ['v', 'o']: # Out / Variadic Mode
proargdefaultvals.insert(cnt, '')
elif m == 't': # Table Mode
proargmodes_fltrd.remove(m)
@ -532,8 +513,7 @@ class EdbFuncView(PGChildNodeView, DataTypeReader):
"""
SQL = render_template(
"/".join([self.sql_template_path, 'get_body.sql']),
scid=scid,
pkgid=pkgid)
edbfnid=edbfnid)
status, res = self.conn.execute_dict(SQL)
if not status:
@ -543,7 +523,7 @@ class EdbFuncView(PGChildNodeView, DataTypeReader):
gettext("Could not find the function in the database.")
)
body = self.get_inner(res['rows'][0]['pkgbodysrc'])
body = res['rows'][0]['funcdef']
if body is None:
body = ''

View File

@ -12,6 +12,6 @@ JOIN
pg_namespace nsp ON nsp.oid=pr.pronamespace
AND nsp.nspname={{ nspname|qtLiteral }}
WHERE
proisagg = FALSE
pr.prokind IN ('f', 'w')
AND typname NOT IN ('trigger', 'event_trigger')
AND pr.proname = {{ name|qtLiteral }};

View File

@ -5,7 +5,7 @@ SELECT pg_proc.oid,
proargnames AS argnames,
pronargdefaults,
oidvectortypes(proargtypes) AS proargtypenames,
proargmodes,
proargdeclaredmodes AS proargmodes,
proargnames,
pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pg_get_userbyid(proowner) AS funcowner,
@ -17,7 +17,7 @@ SELECT pg_proc.oid,
WHEN proaccess = '-' THEN 'Private'
ELSE 'Unknown' END AS visibility
FROM pg_proc, pg_namespace, pg_language lng
WHERE format_type(prorettype, NULL) != 'void'
WHERE pr.prokind IN ('f', 'w')
AND pronamespace = {{pkgid}}::oid
AND pg_proc.pronamespace = pg_namespace.oid
AND lng.oid=prolang

View File

@ -1,5 +0,0 @@
SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc
FROM pg_namespace nsp
LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
WHERE nspparent = {{scid}}::oid
AND nsp.oid = {{pkgid}}::oid;

View File

@ -1,11 +0,0 @@
SELECT pg_proc.oid,
pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name,
pg_get_userbyid(proowner) AS funcowner
FROM pg_proc, pg_namespace
WHERE format_type(prorettype, NULL) != 'void'
{% if fnid %}
AND pg_proc.oid = {{ fnid|qtLiteral }}
{% endif %}
AND pronamespace = {{ pkgid|qtLiteral }}::oid
AND pg_proc.pronamespace = pg_namespace.oid
ORDER BY pg_proc.proname

View File

@ -1,6 +0,0 @@
SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc
FROM pg_namespace nsp
LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
WHERE nspparent = {{scid}}::oid
AND nsp.oid = {{pkgid}}::oid
AND nspobjecttype = 0;

View File

@ -1,10 +0,0 @@
SELECT pg_proc.oid,
pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name,
pg_get_userbyid(proowner) AS funcowner
FROM pg_proc, pg_namespace
WHERE format_type(prorettype, NULL) != 'void'
{% if fnid %}
AND pg_proc.oid = {{ fnid|qtLiteral }}
{% endif %}
AND pronamespace = {{pkgid|qtLiteral}}::oid
AND pg_proc.pronamespace = pg_namespace.oid

View File

@ -0,0 +1 @@
SELECT pg_get_functiondef({{edbfnid}}::oid) AS funcdef;

View File

@ -2,7 +2,7 @@ SELECT pg_proc.oid,
pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name,
pg_get_userbyid(proowner) AS funcowner
FROM pg_proc, pg_namespace
WHERE format_type(prorettype, NULL) = 'void'
WHERE protype = '0'::char
{% if fnid %}
AND pg_proc.oid = {{ fnid|qtLiteral }}
{% endif %}

View File

@ -5,7 +5,7 @@ SELECT pg_proc.oid,
proargnames AS argnames,
pronargdefaults,
oidvectortypes(proargtypes) AS proargtypenames,
proargmodes,
proargdeclaredmodes AS proargmodes,
proargnames,
pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pg_get_userbyid(proowner) AS funcowner,
@ -17,7 +17,7 @@ SELECT pg_proc.oid,
WHEN proaccess = '-' THEN 'Private'
ELSE 'Unknown' END AS visibility
FROM pg_proc, pg_namespace, pg_language lng
WHERE format_type(prorettype, NULL) = 'void'
WHERE protype = '0'::char
AND pronamespace = {{pkgid}}::oid
AND pg_proc.pronamespace = pg_namespace.oid
AND lng.oid=prolang

View File

@ -12,6 +12,6 @@ JOIN
pg_namespace nsp ON nsp.oid=pr.pronamespace
AND nsp.nspname={{ nspname|qtLiteral }}
WHERE
proisagg = FALSE
pr.prokind = 'p'
AND typname NOT IN ('trigger', 'event_trigger')
AND pr.proname = {{ name|qtLiteral }};

View File

@ -5,7 +5,7 @@ SELECT pg_proc.oid,
proargnames AS argnames,
pronargdefaults,
oidvectortypes(proargtypes) AS proargtypenames,
proargmodes,
proargdeclaredmodes AS proargmodes,
proargnames,
pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pg_get_userbyid(proowner) AS funcowner,
@ -17,7 +17,7 @@ SELECT pg_proc.oid,
WHEN proaccess = '-' THEN 'Private'
ELSE 'Unknown' END AS visibility
FROM pg_proc, pg_namespace, pg_language lng
WHERE format_type(prorettype, NULL) != 'void'
WHERE pr.prokind = 'p'
AND pronamespace = {{pkgid}}::oid
AND pg_proc.pronamespace = pg_namespace.oid
AND lng.oid=prolang

View File

@ -1,5 +0,0 @@
SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc
FROM pg_namespace nsp
LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
WHERE nspparent = {{scid}}::oid
AND nsp.oid = {{pkgid}}::oid;

View File

@ -1,3 +0,0 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

@ -1,6 +0,0 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

@ -1,8 +0,0 @@
SELECT
calls AS {{ conn|qtIdent(_('Number of calls')) }},
total_time AS {{ conn|qtIdent(_('Total time')) }},
self_time AS {{ conn|qtIdent(_('Self time')) }}
FROM
pg_stat_user_functions
WHERE
funcid = {{fnid}}::OID

View File

@ -1,6 +0,0 @@
SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc
FROM pg_namespace nsp
LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
WHERE nspparent = {{scid}}::oid
AND nsp.oid = {{pkgid}}::oid
AND nspobjecttype = 0;

View File

@ -1,3 +0,0 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

@ -1,6 +0,0 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

@ -1,8 +0,0 @@
SELECT
calls AS {{ conn|qtIdent(_('Number of calls')) }},
total_time AS {{ conn|qtIdent(_('Total time')) }},
self_time AS {{ conn|qtIdent(_('Self time')) }}
FROM
pg_stat_user_functions
WHERE
funcid = {{fnid}}::OID

View File

@ -0,0 +1 @@
SELECT pg_get_functiondef({{edbfnid}}::oid) AS funcdef;

View File

@ -2,7 +2,7 @@ SELECT pg_proc.oid,
pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name,
pg_get_userbyid(proowner) AS funcowner
FROM pg_proc, pg_namespace
WHERE format_type(prorettype, NULL) = 'void'
WHERE protype = '1'::char
{% if fnid %}
AND pg_proc.oid = {{ fnid|qtLiteral }}
{% endif %}

View File

@ -17,7 +17,7 @@ SELECT pg_proc.oid,
WHEN proaccess = '-' THEN 'Private'
ELSE 'Unknown' END AS visibility
FROM pg_proc, pg_namespace, pg_language lng
WHERE format_type(prorettype, NULL) = 'void'
WHERE protype = '1'::char
AND pronamespace = {{pkgid}}::oid
AND pg_proc.pronamespace = pg_namespace.oid
AND lng.oid=prolang

View File

@ -0,0 +1,16 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
class PackageEDBFuncsTestGenerator(BaseTestGenerator):
def runTest(self):
return

View File

@ -0,0 +1,138 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import json
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.utils import server_utils as server_utils
from pgadmin.utils.route import BaseTestGenerator
from regression import parent_node_dict
from regression.python_test_utils import test_utils as utils
class PackageEDBFuncsGetTestCase(BaseTestGenerator):
""" This class will fetch functions/procedures of package
under test schema. """
skip_on_database = ['gpdb', 'pg']
scenarios = [
# Fetching default URL for package node.
('Fetch Package Functions/Procedures URL', dict(
url='/browser/{0}/nodes/'))
]
def setUp(self):
super(PackageEDBFuncsGetTestCase, self).setUp()
schema_info = parent_node_dict["schema"][-1]
self.schema_id = schema_info["schema_id"]
self.schema_name = schema_info["schema_name"]
self.db_name = parent_node_dict["database"][-1]["db_name"]
self.pkg_name = "pkg_%s" % str(uuid.uuid4())[1:8]
self.proc_name = "proc_%s" % str(uuid.uuid4())[1:8]
self.func_name = "func_%s" % str(uuid.uuid4())[1:8]
self.server_id = schema_info["server_id"]
self.db_id = schema_info["db_id"]
server_con = server_utils.connect_server(self, self.server_id)
connection = utils.get_db_connection(self.db_name,
self.server['username'],
self.server['db_password'],
self.server['host'],
self.server['port'],
self.server['sslmode'])
pg_cursor = connection.cursor()
query = """
CREATE OR REPLACE PACKAGE %s.%s
IS
emp_name character varying(10);
PROCEDURE %s(INOUT p_empno numeric);
FUNCTION %s() RETURN integer;
END %s;
CREATE OR REPLACE PACKAGE BODY %s.%s
IS
v_counter integer;
PROCEDURE %s(INOUT p_empno numeric) IS
BEGIN
SELECT ename INTO emp_name FROM emp WHERE empno = p_empno;
v_counter := v_counter + 1;
END;
FUNCTION %s() RETURN integer IS
BEGIN
RETURN v_counter;
END;
END %s;""" % (self.schema_name, self.pkg_name, self.proc_name,
self.func_name, self.pkg_name, self.schema_name,
self.pkg_name, self.proc_name, self.func_name,
self.pkg_name)
pg_cursor.execute(query)
connection.commit()
# Get 'oid' from newly created package
pg_cursor.execute("SELECT oid FROM pg_namespace"
" WHERE nspname='%s'" %
self.pkg_name)
self.package_id = pg_cursor.fetchone()[0]
connection.close()
def runTest(self):
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if not db_con["info"] == "Database connected.":
raise Exception("Could not connect to database.")
schema_response = schema_utils.verify_schemas(self.server,
self.db_name,
self.schema_name)
if not schema_response:
raise Exception("Could not find the schema.")
# Fetch Package function
url = self.url.format('edbfunc') + str(
utils.SERVER_GROUP) + '/' + str(self.server_id) + '/' + str(
self.db_id) + '/' + str(self.schema_id) + '/' + str(
self.package_id) + "/"
response = self.tester.get(url,
content_type='html/json')
response_data = json.loads(response.data.decode('utf-8'))
self.assertEquals(response.status_code, 200)
self.assertEquals(len(response_data['data']), 1)
self.assertEquals(response_data['data'][0]['label'],
self.func_name + '()')
self.assertEquals(response_data['data'][0]['_type'], 'edbfunc')
# Fetch Package procedure
url = self.url.format('edbproc') + str(
utils.SERVER_GROUP) + '/' + str(self.server_id) + '/' + str(
self.db_id) + '/' + str(self.schema_id) + '/' + str(
self.package_id) + "/"
response = self.tester.get(url,
content_type='html/json')
response_data = json.loads(response.data.decode('utf-8'))
self.assertEquals(response.status_code, 200)
self.assertEquals(len(response_data['data']), 1)
self.assertIn(self.proc_name, response_data['data'][0]['label'])
self.assertIn("INOUT", response_data['data'][0]['label'])
self.assertEquals(response_data['data'][0]['_type'], 'edbproc')
def tearDown(self):
"""This function disconnect the test database."""
database_utils.disconnect_database(self, self.server_id,
self.db_id)

View File

@ -650,7 +650,6 @@ define([
// Start pooling again
pgTools.DirectDebug.polling_timeout_idle = false;
pgTools.DirectDebug.is_polling_required = true;
self.poll_end_execution_result(trans_id);
self.poll_result(trans_id);
if (restart_dbg) {

View File

@ -26,7 +26,11 @@ SELECT
pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')
END AS proargtypes,
pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,
{% if is_ppas_database %}
pg_catalog.array_to_string(proargdeclaredmodes, ',') AS proargmodes,
{% else %}
pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,
{% endif %}
{% if is_ppas_database %}
CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,