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
parent
39b41d7b24
commit
8869fd6eb3
|
@ -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.
|
||||
|
|
|
@ -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 = ''
|
||||
|
|
|
@ -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 }};
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
SELECT pg_get_functiondef({{edbfnid}}::oid) AS funcdef;
|
|
@ -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 %}
|
|
@ -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
|
|
@ -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 }};
|
|
@ -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
|
|
@ -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;
|
|
@ -1,3 +0,0 @@
|
|||
SELECT proname AS name
|
||||
FROM pg_proc
|
||||
WHERE oid = {{edbfnid}}::oid
|
|
@ -1,6 +0,0 @@
|
|||
SELECT
|
||||
nspname
|
||||
FROM
|
||||
pg_namespace
|
||||
WHERE
|
||||
oid = {{ scid }}::oid;
|
|
@ -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
|
|
@ -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;
|
|
@ -1,3 +0,0 @@
|
|||
SELECT proname AS name
|
||||
FROM pg_proc
|
||||
WHERE oid = {{edbfnid}}::oid
|
|
@ -1,6 +0,0 @@
|
|||
SELECT
|
||||
nspname
|
||||
FROM
|
||||
pg_namespace
|
||||
WHERE
|
||||
oid = {{ scid }}::oid;
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
SELECT pg_get_functiondef({{edbfnid}}::oid) AS funcdef;
|
|
@ -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 %}
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue